2007-03-22 10:30:00 -07:00
|
|
|
|
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
|
|
|
/* ***** BEGIN LICENSE BLOCK *****
|
|
|
|
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
|
|
|
|
*
|
|
|
|
|
* The contents of this file are subject to the Mozilla Public License
|
|
|
|
|
* Version 1.1 (the "License"); you may not use this file except in
|
|
|
|
|
* compliance with the License. You may obtain a copy of the License at
|
|
|
|
|
* http://www.mozilla.org/MPL/
|
|
|
|
|
*
|
|
|
|
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
|
|
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
|
|
|
* for the specific language governing rights and limitations under the
|
|
|
|
|
* License.
|
|
|
|
|
*
|
|
|
|
|
* The Original Code is mozilla.org code.
|
|
|
|
|
*
|
|
|
|
|
* The Initial Developer of the Original Code is
|
|
|
|
|
* Netscape Communications Corporation.
|
|
|
|
|
* Portions created by the Initial Developer are Copyright (C) 1998
|
|
|
|
|
* the Initial Developer. All Rights Reserved.
|
|
|
|
|
*
|
|
|
|
|
* Contributor(s):
|
|
|
|
|
* Simon Fraser <sfraser@netscape.com>
|
|
|
|
|
* Josh Aas <josh@mozilla.com>
|
|
|
|
|
*
|
|
|
|
|
* Alternatively, the contents of this file may be used under the terms of
|
|
|
|
|
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
|
|
|
|
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
|
|
|
|
* in which case the provisions of the GPL or the LGPL are applicable instead
|
|
|
|
|
* of those above. If you wish to allow use of your version of this file only
|
|
|
|
|
* under the terms of either the GPL or the LGPL, and not to allow others to
|
|
|
|
|
* use your version of this file under the terms of the MPL, indicate your
|
|
|
|
|
* decision by deleting the provisions above and replace them with the notice
|
|
|
|
|
* and other provisions required by the GPL or the LGPL. If you do not delete
|
|
|
|
|
* the provisions above, a recipient may use your version of this file under
|
|
|
|
|
* the terms of any one of the MPL, the GPL or the LGPL.
|
|
|
|
|
*
|
|
|
|
|
* ***** END LICENSE BLOCK ***** */
|
|
|
|
|
|
|
|
|
|
#include "nsToolkit.h"
|
|
|
|
|
|
|
|
|
|
#include <ctype.h>
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
|
|
|
|
|
#include <mach/mach_port.h>
|
|
|
|
|
#include <mach/mach_interface.h>
|
|
|
|
|
#include <mach/mach_init.h>
|
|
|
|
|
|
2007-05-17 19:06:59 -07:00
|
|
|
|
#import <Cocoa/Cocoa.h>
|
2007-03-22 10:30:00 -07:00
|
|
|
|
#import <Carbon/Carbon.h>
|
|
|
|
|
#import <IOKit/pwr_mgt/IOPMLib.h>
|
|
|
|
|
#import <IOKit/IOMessage.h>
|
|
|
|
|
|
2007-07-17 13:29:39 -07:00
|
|
|
|
#include "nsCocoaUtils.h"
|
2008-02-20 15:47:05 -08:00
|
|
|
|
#include "nsObjCExceptions.h"
|
2007-07-17 13:29:39 -07:00
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
|
#include "nsWidgetAtoms.h"
|
2007-05-17 19:06:59 -07:00
|
|
|
|
#include "nsIRollupListener.h"
|
|
|
|
|
#include "nsIWidget.h"
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
|
|
#include "nsIObserverService.h"
|
|
|
|
|
#include "nsIServiceManager.h"
|
2007-08-15 14:03:18 -07:00
|
|
|
|
#include "nsIPrefService.h"
|
|
|
|
|
#include "nsIPrefBranch.h"
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
2007-05-17 19:06:59 -07:00
|
|
|
|
// defined in nsChildView.mm
|
|
|
|
|
extern nsIRollupListener * gRollupListener;
|
|
|
|
|
extern nsIWidget * gRollupWidget;
|
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
|
static io_connect_t gRootPort = MACH_PORT_NULL;
|
|
|
|
|
|
|
|
|
|
// Static thread local storage index of the Toolkit
|
|
|
|
|
// object associated with a given thread...
|
|
|
|
|
static PRUintn gToolkitTLSIndex = 0;
|
|
|
|
|
|
|
|
|
|
nsToolkit::nsToolkit()
|
|
|
|
|
: mInited(false)
|
|
|
|
|
, mSleepWakeNotificationRLS(nsnull)
|
2007-07-17 13:29:39 -07:00
|
|
|
|
, mEventMonitorHandler(nsnull)
|
|
|
|
|
, mEventTapPort(nsnull)
|
|
|
|
|
, mEventTapRLS(nsnull)
|
2007-03-22 10:30:00 -07:00
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
nsToolkit::~nsToolkit()
|
|
|
|
|
{
|
|
|
|
|
RemoveSleepWakeNotifcations();
|
2007-07-17 13:29:39 -07:00
|
|
|
|
UnregisterAllProcessMouseEventHandlers();
|
2007-03-22 10:30:00 -07:00
|
|
|
|
// Remove the TLS reference to the toolkit...
|
|
|
|
|
PR_SetThreadPrivate(gToolkitTLSIndex, nsnull);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NS_IMPL_THREADSAFE_ISUPPORTS1(nsToolkit, nsIToolkit);
|
|
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
|
nsToolkit::Init(PRThread * aThread)
|
|
|
|
|
{
|
|
|
|
|
nsWidgetAtoms::RegisterAtoms();
|
|
|
|
|
|
|
|
|
|
mInited = true;
|
|
|
|
|
|
|
|
|
|
RegisterForSleepWakeNotifcations();
|
2007-05-17 19:06:59 -07:00
|
|
|
|
RegisterForAllProcessMouseEvents();
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
nsToolkit* NS_CreateToolkitInstance()
|
|
|
|
|
{
|
|
|
|
|
return new nsToolkit();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
nsToolkit::PostSleepWakeNotification(const char* aNotification)
|
|
|
|
|
{
|
|
|
|
|
nsCOMPtr<nsIObserverService> observerService = do_GetService("@mozilla.org/observer-service;1");
|
|
|
|
|
if (observerService)
|
|
|
|
|
observerService->NotifyObservers(nsnull, aNotification, nsnull);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// http://developer.apple.com/documentation/DeviceDrivers/Conceptual/IOKitFundamentals/PowerMgmt/chapter_10_section_3.html
|
|
|
|
|
static void ToolkitSleepWakeCallback(void *refCon, io_service_t service, natural_t messageType, void * messageArgument)
|
|
|
|
|
{
|
2008-02-20 15:47:05 -08:00
|
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
|
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
|
switch (messageType)
|
|
|
|
|
{
|
|
|
|
|
case kIOMessageSystemWillSleep:
|
|
|
|
|
// System is going to sleep now.
|
|
|
|
|
nsToolkit::PostSleepWakeNotification("sleep_notification");
|
|
|
|
|
::IOAllowPowerChange(gRootPort, (long)messageArgument);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case kIOMessageCanSystemSleep:
|
|
|
|
|
// In this case, the computer has been idle for several minutes
|
|
|
|
|
// and will sleep soon so you must either allow or cancel
|
|
|
|
|
// this notification. Important: if you don’t respond, there will
|
|
|
|
|
// be a 30-second timeout before the computer sleeps.
|
|
|
|
|
// In Mozilla's case, we always allow sleep.
|
|
|
|
|
::IOAllowPowerChange(gRootPort,(long)messageArgument);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case kIOMessageSystemHasPoweredOn:
|
|
|
|
|
// Handle wakeup.
|
|
|
|
|
nsToolkit::PostSleepWakeNotification("wake_notification");
|
|
|
|
|
break;
|
|
|
|
|
}
|
2008-02-20 15:47:05 -08:00
|
|
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK;
|
2007-03-22 10:30:00 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
nsresult
|
|
|
|
|
nsToolkit::RegisterForSleepWakeNotifcations()
|
|
|
|
|
{
|
2008-02-20 15:47:05 -08:00
|
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
|
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
|
IONotificationPortRef notifyPortRef;
|
|
|
|
|
|
|
|
|
|
NS_ASSERTION(!mSleepWakeNotificationRLS, "Already registered for sleep/wake");
|
|
|
|
|
|
|
|
|
|
gRootPort = ::IORegisterForSystemPower(0, ¬ifyPortRef, ToolkitSleepWakeCallback, &mPowerNotifier);
|
|
|
|
|
if (gRootPort == MACH_PORT_NULL) {
|
2009-08-14 07:09:00 -07:00
|
|
|
|
NS_ERROR("IORegisterForSystemPower failed");
|
2007-03-22 10:30:00 -07:00
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
mSleepWakeNotificationRLS = ::IONotificationPortGetRunLoopSource(notifyPortRef);
|
|
|
|
|
::CFRunLoopAddSource(::CFRunLoopGetCurrent(),
|
|
|
|
|
mSleepWakeNotificationRLS,
|
|
|
|
|
kCFRunLoopDefaultMode);
|
|
|
|
|
|
|
|
|
|
return NS_OK;
|
2008-02-20 15:47:05 -08:00
|
|
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
|
2007-03-22 10:30:00 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
nsToolkit::RemoveSleepWakeNotifcations()
|
|
|
|
|
{
|
2008-02-20 15:47:05 -08:00
|
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
|
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
|
if (mSleepWakeNotificationRLS) {
|
|
|
|
|
::IODeregisterForSystemPower(&mPowerNotifier);
|
|
|
|
|
::CFRunLoopRemoveSource(::CFRunLoopGetCurrent(),
|
|
|
|
|
mSleepWakeNotificationRLS,
|
|
|
|
|
kCFRunLoopDefaultMode);
|
|
|
|
|
|
|
|
|
|
mSleepWakeNotificationRLS = nsnull;
|
|
|
|
|
}
|
2008-02-20 15:47:05 -08:00
|
|
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK;
|
2007-03-22 10:30:00 -07:00
|
|
|
|
}
|
|
|
|
|
|
2007-07-17 13:29:39 -07:00
|
|
|
|
// We shouldn't do anything here. See RegisterForAllProcessMouseEvents() for
|
|
|
|
|
// the reason why.
|
|
|
|
|
static OSStatus EventMonitorHandler(EventHandlerCallRef aCaller, EventRef aEvent, void* aRefcon)
|
2007-05-17 19:06:59 -07:00
|
|
|
|
{
|
2007-07-17 13:29:39 -07:00
|
|
|
|
return eventNotHandledErr;
|
|
|
|
|
}
|
2007-05-17 19:06:59 -07:00
|
|
|
|
|
2007-07-17 13:29:39 -07:00
|
|
|
|
// Converts aPoint from the CoreGraphics "global display coordinate" system
|
|
|
|
|
// (which includes all displays/screens and has a top-left origin) to its
|
|
|
|
|
// (presumed) Cocoa counterpart (assumed to be the same as the "screen
|
|
|
|
|
// coordinates" system), which has a bottom-left origin.
|
|
|
|
|
static NSPoint ConvertCGGlobalToCocoaScreen(CGPoint aPoint)
|
|
|
|
|
{
|
|
|
|
|
NSPoint cocoaPoint;
|
|
|
|
|
cocoaPoint.x = aPoint.x;
|
2007-12-05 15:17:08 -08:00
|
|
|
|
cocoaPoint.y = nsCocoaUtils::FlippedScreenY(aPoint.y);
|
2007-07-17 13:29:39 -07:00
|
|
|
|
return cocoaPoint;
|
2007-05-17 19:06:59 -07:00
|
|
|
|
}
|
|
|
|
|
|
2007-07-17 13:29:39 -07:00
|
|
|
|
// Since our event tap is "listen only", events arrive here a little after
|
|
|
|
|
// they've already been processed.
|
|
|
|
|
static CGEventRef EventTapCallback(CGEventTapProxy proxy, CGEventType type, CGEventRef event, void *refcon)
|
|
|
|
|
{
|
2008-02-20 15:47:05 -08:00
|
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
|
|
|
|
|
|
2007-07-17 13:29:39 -07:00
|
|
|
|
if ((type == kCGEventTapDisabledByUserInput) ||
|
|
|
|
|
(type == kCGEventTapDisabledByTimeout))
|
|
|
|
|
return event;
|
|
|
|
|
if (!gRollupWidget || !gRollupListener || [NSApp isActive])
|
|
|
|
|
return event;
|
|
|
|
|
// Don't bother with rightMouseDown events here -- because of the delay,
|
|
|
|
|
// we'll end up closing browser context menus that we just opened. Since
|
|
|
|
|
// these events usually raise a context menu, we'll handle them by hooking
|
|
|
|
|
// the @"com.apple.HIToolbox.beginMenuTrackingNotification" distributed
|
|
|
|
|
// notification (in nsAppShell.mm's AppShellDelegate).
|
|
|
|
|
if (type == kCGEventRightMouseDown)
|
|
|
|
|
return event;
|
|
|
|
|
NSWindow *ctxMenuWindow = (NSWindow*) gRollupWidget->GetNativeData(NS_NATIVE_WINDOW);
|
|
|
|
|
if (!ctxMenuWindow)
|
|
|
|
|
return event;
|
|
|
|
|
NSPoint screenLocation = ConvertCGGlobalToCocoaScreen(CGEventGetLocation(event));
|
|
|
|
|
// Don't roll up the rollup widget if our mouseDown happens over it (doing
|
|
|
|
|
// so would break the corresponding context menu).
|
|
|
|
|
if (NSPointInRect(screenLocation, [ctxMenuWindow frame]))
|
|
|
|
|
return event;
|
2009-06-12 11:23:16 -07:00
|
|
|
|
gRollupListener->Rollup(nsnull, nsnull);
|
2007-07-17 13:29:39 -07:00
|
|
|
|
return event;
|
2008-02-20 15:47:05 -08:00
|
|
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NULL);
|
2007-07-17 13:29:39 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Cocoa Firefox's use of custom context menus requires that we explicitly
|
|
|
|
|
// handle mouse events from other processes that the OS handles
|
|
|
|
|
// "automatically" for native context menus -- mouseMoved events so that
|
|
|
|
|
// right-click context menus work properly when our browser doesn't have the
|
|
|
|
|
// focus (bmo bug 368077), and mouseDown events so that our browser can
|
|
|
|
|
// dismiss a context menu when a mouseDown happens in another process (bmo
|
|
|
|
|
// bug 339945).
|
2007-05-17 19:06:59 -07:00
|
|
|
|
void
|
|
|
|
|
nsToolkit::RegisterForAllProcessMouseEvents()
|
|
|
|
|
{
|
2008-02-20 15:47:05 -08:00
|
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
|
|
|
|
|
|
2007-08-15 14:03:18 -07:00
|
|
|
|
// Don't do this for apps that (like Camino) use native context menus.
|
|
|
|
|
nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
|
|
|
|
|
if (prefs) {
|
|
|
|
|
PRBool useNativeContextMenus;
|
|
|
|
|
nsresult rv = prefs->GetBoolPref("ui.use_native_popup_windows",
|
|
|
|
|
&useNativeContextMenus);
|
|
|
|
|
if (NS_SUCCEEDED(rv) && useNativeContextMenus)
|
|
|
|
|
return;
|
|
|
|
|
}
|
2007-07-17 13:29:39 -07:00
|
|
|
|
if (!mEventMonitorHandler) {
|
|
|
|
|
// Installing a handler for particular Carbon events causes the OS to post
|
|
|
|
|
// equivalent Cocoa events to the browser's event stream (the one that
|
|
|
|
|
// passes through [NSApp sendEvent:]). For this reason installing a
|
|
|
|
|
// handler for kEventMouseMoved fixes bmo bug 368077, even though our
|
|
|
|
|
// handler does nothing on mouse-moved events. (Actually it's more
|
|
|
|
|
// accurate to say that the OS (working in a different process) sends
|
|
|
|
|
// events to the window server, from which the OS (acting in the browser's
|
|
|
|
|
// process on its behalf) grabs them and turns them into both Carbon
|
|
|
|
|
// events (which get fed to our handler) and Cocoa events (which get fed
|
|
|
|
|
// to [NSApp sendEvent:]).)
|
|
|
|
|
EventTypeSpec kEvents[] = {{kEventClassMouse, kEventMouseMoved}};
|
|
|
|
|
InstallEventHandler(GetEventMonitorTarget(), EventMonitorHandler,
|
|
|
|
|
GetEventTypeCount(kEvents), kEvents, 0,
|
|
|
|
|
&mEventMonitorHandler);
|
|
|
|
|
}
|
|
|
|
|
if (!mEventTapRLS) {
|
|
|
|
|
// Using an event tap for mouseDown events (instead of installing a
|
|
|
|
|
// handler for them on the EventMonitor target) works around an Apple
|
|
|
|
|
// bug that causes OS menus (like the Clock menu) not to work properly
|
|
|
|
|
// on OS X 10.4.X and below (bmo bug 381448).
|
|
|
|
|
// We install our event tap "listen only" to get around yet another Apple
|
|
|
|
|
// bug -- when we install it as an event filter on any kind of mouseDown
|
|
|
|
|
// event, that kind of event stops working in the main menu, and usually
|
|
|
|
|
// mouse event processing stops working in all apps in the current login
|
|
|
|
|
// session (so the entire OS appears to be hung)! The downside of
|
|
|
|
|
// installing listen-only is that events arrive at our handler slightly
|
|
|
|
|
// after they've already been processed.
|
|
|
|
|
mEventTapPort = CGEventTapCreate(kCGSessionEventTap,
|
|
|
|
|
kCGHeadInsertEventTap,
|
|
|
|
|
kCGEventTapOptionListenOnly,
|
|
|
|
|
CGEventMaskBit(kCGEventLeftMouseDown)
|
|
|
|
|
| CGEventMaskBit(kCGEventRightMouseDown)
|
|
|
|
|
| CGEventMaskBit(kCGEventOtherMouseDown),
|
|
|
|
|
EventTapCallback,
|
|
|
|
|
nsnull);
|
|
|
|
|
if (!mEventTapPort)
|
|
|
|
|
return;
|
|
|
|
|
mEventTapRLS = CFMachPortCreateRunLoopSource(nsnull, mEventTapPort, 0);
|
|
|
|
|
if (!mEventTapRLS) {
|
|
|
|
|
CFRelease(mEventTapPort);
|
|
|
|
|
mEventTapPort = nsnull;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
CFRunLoopAddSource(CFRunLoopGetCurrent(), mEventTapRLS, kCFRunLoopDefaultMode);
|
|
|
|
|
}
|
2008-02-20 15:47:05 -08:00
|
|
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK;
|
2007-07-17 13:29:39 -07:00
|
|
|
|
}
|
2007-05-17 19:06:59 -07:00
|
|
|
|
|
2007-07-17 13:29:39 -07:00
|
|
|
|
void
|
|
|
|
|
nsToolkit::UnregisterAllProcessMouseEventHandlers()
|
|
|
|
|
{
|
2008-02-20 15:47:05 -08:00
|
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
|
|
|
|
|
|
2007-07-17 13:29:39 -07:00
|
|
|
|
if (mEventMonitorHandler) {
|
|
|
|
|
RemoveEventHandler(mEventMonitorHandler);
|
|
|
|
|
mEventMonitorHandler = nsnull;
|
|
|
|
|
}
|
|
|
|
|
if (mEventTapRLS) {
|
|
|
|
|
CFRunLoopRemoveSource(CFRunLoopGetCurrent(), mEventTapRLS,
|
|
|
|
|
kCFRunLoopDefaultMode);
|
|
|
|
|
CFRelease(mEventTapRLS);
|
|
|
|
|
mEventTapRLS = nsnull;
|
|
|
|
|
}
|
|
|
|
|
if (mEventTapPort) {
|
2009-01-07 15:03:56 -08:00
|
|
|
|
// mEventTapPort must be invalidated as well as released. Otherwise the
|
|
|
|
|
// event tap doesn't get destroyed until the browser process ends (it
|
|
|
|
|
// keeps showing up in the list returned by CGGetEventTapList()).
|
|
|
|
|
CFMachPortInvalidate(mEventTapPort);
|
2007-07-17 13:29:39 -07:00
|
|
|
|
CFRelease(mEventTapPort);
|
|
|
|
|
mEventTapPort = nsnull;
|
|
|
|
|
}
|
2008-02-20 15:47:05 -08:00
|
|
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK;
|
2007-05-17 19:06:59 -07:00
|
|
|
|
}
|
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
|
// Return the nsIToolkit for the current thread. If a toolkit does not
|
|
|
|
|
// yet exist, then one will be created...
|
2008-02-20 15:47:05 -08:00
|
|
|
|
NS_IMETHODIMP NS_GetCurrentToolkit(nsIToolkit* *aResult)
|
2007-03-22 10:30:00 -07:00
|
|
|
|
{
|
2008-02-20 15:47:05 -08:00
|
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
|
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
|
NS_ENSURE_ARG_POINTER(aResult);
|
|
|
|
|
*aResult = nsnull;
|
|
|
|
|
|
|
|
|
|
// Create the TLS index the first time through...
|
|
|
|
|
if (gToolkitTLSIndex == 0) {
|
|
|
|
|
PRStatus status = PR_NewThreadPrivateIndex(&gToolkitTLSIndex, NULL);
|
|
|
|
|
if (PR_FAILURE == status)
|
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Create a new toolkit for this thread...
|
|
|
|
|
nsToolkit* toolkit = (nsToolkit*)PR_GetThreadPrivate(gToolkitTLSIndex);
|
|
|
|
|
if (!toolkit) {
|
|
|
|
|
toolkit = NS_CreateToolkitInstance();
|
|
|
|
|
if (!toolkit)
|
|
|
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
|
|
|
|
|
|
|
|
NS_ADDREF(toolkit);
|
|
|
|
|
toolkit->Init(PR_GetCurrentThread());
|
|
|
|
|
//
|
|
|
|
|
// The reference stored in the TLS is weak. It is removed in the
|
|
|
|
|
// nsToolkit destructor...
|
|
|
|
|
//
|
|
|
|
|
PR_SetThreadPrivate(gToolkitTLSIndex, (void*)toolkit);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
NS_ADDREF(toolkit);
|
|
|
|
|
}
|
|
|
|
|
*aResult = toolkit;
|
|
|
|
|
return NS_OK;
|
2008-02-20 15:47:05 -08:00
|
|
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
|
2007-03-22 10:30:00 -07:00
|
|
|
|
}
|
|
|
|
|
|
2009-08-12 15:32:41 -07:00
|
|
|
|
PRInt32 nsToolkit::OSXVersion()
|
2007-03-22 10:30:00 -07:00
|
|
|
|
{
|
2008-02-20 15:47:05 -08:00
|
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
|
|
|
|
|
|
2009-08-12 15:32:41 -07:00
|
|
|
|
static PRInt32 gOSXVersion = 0x0;
|
2007-03-22 10:30:00 -07:00
|
|
|
|
if (gOSXVersion == 0x0) {
|
2009-08-12 15:32:41 -07:00
|
|
|
|
OSErr err = ::Gestalt(gestaltSystemVersion, (SInt32*)&gOSXVersion);
|
2007-03-22 10:30:00 -07:00
|
|
|
|
if (err != noErr) {
|
2009-08-12 15:32:41 -07:00
|
|
|
|
// This should probably be changed when our minimum version changes
|
2007-07-17 17:07:36 -07:00
|
|
|
|
NS_ERROR("Couldn't determine OS X version, assuming 10.4");
|
|
|
|
|
gOSXVersion = MAC_OS_X_VERSION_10_4_HEX;
|
2007-03-22 10:30:00 -07:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return gOSXVersion;
|
2008-02-20 15:47:05 -08:00
|
|
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(0);
|
2007-03-22 10:30:00 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PRBool nsToolkit::OnLeopardOrLater()
|
|
|
|
|
{
|
2009-09-22 00:48:55 -07:00
|
|
|
|
return (OSXVersion() >= MAC_OS_X_VERSION_10_5_HEX);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PRBool nsToolkit::OnSnowLeopardOrLater()
|
|
|
|
|
{
|
|
|
|
|
return (OSXVersion() >= MAC_OS_X_VERSION_10_6_HEX);
|
2007-03-22 10:30:00 -07:00
|
|
|
|
}
|
2008-02-13 07:57:12 -08:00
|
|
|
|
|
|
|
|
|
// An alternative to [NSObject poseAsClass:] that isn't deprecated on OS X
|
|
|
|
|
// Leopard and is available to 64-bit binaries on Leopard and above. Based on
|
|
|
|
|
// ideas and code from http://www.cocoadev.com/index.pl?MethodSwizzling.
|
|
|
|
|
// Since the Method type becomes an opaque type as of Objective-C 2.0, we'll
|
|
|
|
|
// have to switch to using accessor methods like method_exchangeImplementations()
|
|
|
|
|
// when we build 64-bit binaries that use Objective-C 2.0 (on and for Leopard
|
|
|
|
|
// and above). But these accessor methods aren't available in Objective-C 1
|
|
|
|
|
// (or on Tiger). So we need to access Method's members directly for (Tiger-
|
|
|
|
|
// capable) binaries (32-bit or 64-bit) that use Objective-C 1 (as long as we
|
|
|
|
|
// keep supporting Tiger).
|
|
|
|
|
//
|
|
|
|
|
// Be aware that, if aClass doesn't have an orgMethod selector but one of its
|
|
|
|
|
// superclasses does, the method substitution will (in effect) take place in
|
|
|
|
|
// that superclass (rather than in aClass itself). The substitution has
|
|
|
|
|
// effect on the class where it takes place and all of that class's
|
|
|
|
|
// subclasses. In order for method swizzling to work properly, posedMethod
|
|
|
|
|
// needs to be unique in the class where the substitution takes place and all
|
|
|
|
|
// of its subclasses.
|
2008-03-27 09:30:13 -07:00
|
|
|
|
nsresult nsToolkit::SwizzleMethods(Class aClass, SEL orgMethod, SEL posedMethod,
|
|
|
|
|
PRBool classMethods)
|
2008-02-13 07:57:12 -08:00
|
|
|
|
{
|
2008-02-20 15:47:05 -08:00
|
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
|
|
|
|
|
|
2008-03-27 09:30:13 -07:00
|
|
|
|
Method original = nil;
|
|
|
|
|
Method posed = nil;
|
|
|
|
|
|
|
|
|
|
if (classMethods) {
|
|
|
|
|
original = class_getClassMethod(aClass, orgMethod);
|
|
|
|
|
posed = class_getClassMethod(aClass, posedMethod);
|
|
|
|
|
} else {
|
|
|
|
|
original = class_getInstanceMethod(aClass, orgMethod);
|
|
|
|
|
posed = class_getInstanceMethod(aClass, posedMethod);
|
|
|
|
|
}
|
2008-02-13 07:57:12 -08:00
|
|
|
|
|
|
|
|
|
if (!original || !posed)
|
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
|
|
2009-08-12 15:32:41 -07:00
|
|
|
|
#ifdef __LP64__
|
|
|
|
|
method_exchangeImplementations(original, posed);
|
|
|
|
|
#else
|
2008-02-13 07:57:12 -08:00
|
|
|
|
IMP aMethodImp = original->method_imp;
|
|
|
|
|
original->method_imp = posed->method_imp;
|
|
|
|
|
posed->method_imp = aMethodImp;
|
2009-08-12 15:32:41 -07:00
|
|
|
|
#endif
|
2008-02-13 07:57:12 -08:00
|
|
|
|
|
|
|
|
|
return NS_OK;
|
2008-02-20 15:47:05 -08:00
|
|
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
|
2008-02-13 07:57:12 -08:00
|
|
|
|
}
|