New appshell (process Gecko events on demand in Cocoa widgets). b=395397 r=joshmoz+mark sr=roc a=joshmoz

git-svn-id: svn://10.0.0.236/trunk@236311 18797224-902f-48f8-a5cc-f745e15eee43
This commit is contained in:
smichaud%pobox.com 2007-09-19 19:17:06 +00:00
parent 67f8a80610
commit ac9a979a12
5 changed files with 294 additions and 130 deletions

View File

@ -50,20 +50,20 @@
class nsAppShell : public nsBaseAppShell
{
public:
NS_IMETHODIMP ResumeNative(void);
NS_IMETHOD ResumeNative(void);
nsAppShell();
nsresult Init();
NS_IMETHOD Run(void);
NS_IMETHOD Exit(void);
NS_IMETHOD OnProcessNextEvent(nsIThreadInternal *aThread, PRBool aMayWait,
PRUint32 aRecursionDepth);
NS_IMETHOD AfterProcessNextEvent(nsIThreadInternal *aThread,
PRUint32 aRecursionDepth);
// public only to be visible to Objective-C code that must call it
void ProcessGeckoEvents();
void WillTerminate();
protected:
@ -72,16 +72,25 @@ protected:
virtual void ScheduleNativeEventCallback();
virtual PRBool ProcessNextNativeEvent(PRBool aMayWait);
static void ProcessGeckoEvents(void* aInfo);
protected:
NSAutoreleasePool* mMainPool;
CFMutableArrayRef mAutoreleasePools;
NSPort* mPort;
AppShellDelegate* mDelegate;
CFRunLoopRef mCFRunLoop;
CFRunLoopSourceRef mCFRunLoopSource;
PRPackedBool mRunningEventLoop;
PRPackedBool mStarted;
PRPackedBool mTerminated;
PRPackedBool mSkippedNativeCallback;
// mHadMoreEventsCount and kHadMoreEventsCountMax are used in
// ProcessNextNativeEvent().
PRUint32 mHadMoreEventsCount;
static const PRUint32 kHadMoreEventsCountMax = 10;
};
#endif // nsAppShell_h_

View File

@ -49,28 +49,38 @@
#include "nsString.h"
#include "nsIRollupListener.h"
#include "nsIWidget.h"
#include "nsThreadUtils.h"
#include "nsIWindowMediator.h"
#include "nsServiceManagerUtils.h"
#include "nsIInterfaceRequestor.h"
#include "nsIWebBrowserChrome.h"
// defined in nsChildView.mm
extern nsIRollupListener * gRollupListener;
extern nsIWidget * gRollupWidget;
// defined in nsCocoaWindow.mm
extern PRInt32 gXULModalLevel;
@interface NSApplication (Undocumented)
// Present in all versions of OS X from (at least) 10.2.8 through 10.5.
- (BOOL)_isRunningModal;
@end
// AppShellDelegate
//
// Cocoa bridge class. An object of this class is used as an NSPort
// delegate called on the main thread when Gecko wants to interrupt
// the native run loop.
// Cocoa bridge class. An object of this class is registered to receive
// notifications.
//
@interface AppShellDelegate : NSObject
{
@private
nsAppShell* mAppShell;
nsresult mRunRV;
}
- (id)initWithAppShell:(nsAppShell*)aAppShell;
- (void)handlePortMessage:(NSPortMessage*)aPortMessage;
- (void)runAppShell;
- (nsresult)rvFromRun;
- (void)applicationWillTerminate:(NSNotification*)aNotification;
- (void)beginMenuTracking:(NSNotification*)aNotification;
@end
@ -92,11 +102,14 @@ nsAppShell::ResumeNative(void)
nsAppShell::nsAppShell()
: mAutoreleasePools(nsnull)
, mPort(nil)
, mDelegate(nil)
, mDelegate(nsnull)
, mCFRunLoop(NULL)
, mCFRunLoopSource(NULL)
, mRunningEventLoop(PR_FALSE)
, mStarted(PR_FALSE)
, mTerminated(PR_FALSE)
, mSkippedNativeCallback(PR_FALSE)
, mHadMoreEventsCount(0)
{
// mMainPool sits low on the autorelease pool stack to serve as a catch-all
// for autoreleased objects on this thread. Because it won't be popped
@ -109,33 +122,38 @@ nsAppShell::nsAppShell()
nsAppShell::~nsAppShell()
{
if (mCFRunLoop) {
if (mCFRunLoopSource) {
::CFRunLoopRemoveSource(mCFRunLoop, mCFRunLoopSource,
kCFRunLoopCommonModes);
::CFRelease(mCFRunLoopSource);
}
::CFRelease(mCFRunLoop);
}
if (mAutoreleasePools) {
NS_ASSERTION(::CFArrayGetCount(mAutoreleasePools) == 0,
"nsAppShell destroyed without popping all autorelease pools");
::CFRelease(mAutoreleasePools);
}
if (mPort) {
[[NSRunLoop currentRunLoop] removePort:mPort forMode:NSDefaultRunLoopMode];
[mPort release];
}
[mDelegate release];
[mMainPool release];
}
// Init
//
// Loads the nib (see bug 316076c21) and sets up the NSPort used to
// interrupt the main Cocoa event loop.
// Loads the nib (see bug 316076c21) and sets up the CFRunLoopSource used to
// interrupt the main native run loop.
//
// public
nsresult
nsAppShell::Init()
{
// No event loop is running yet. Avoid autoreleasing objects to
// mMainPool. The appshell retains objects it needs to be long-lived
// and will release them as appropriate.
// No event loop is running yet (unless Camino is running, or another
// embedding app that uses NSApplicationMain()). Avoid autoreleasing
// objects to mMainPool. The appshell retains objects it needs to be
// long-lived and will release them as appropriate.
NSAutoreleasePool* localPool = [[NSAutoreleasePool alloc] init];
// mAutoreleasePools is used as a stack of NSAutoreleasePool objects created
@ -157,7 +175,12 @@ nsAppShell::Init()
rv = nibFile->GetNativePath(nibPath);
NS_ENSURE_SUCCESS(rv, rv);
// This call initializes NSApplication.
// This call initializes NSApplication unless:
// 1) we're using xre -- NSApp's already been initialized by
// MacApplicationDelegate.mm's EnsureUseCocoaDockAPI().
// 2) Camino is running (or another embedding app that uses
// NSApplicationMain()) -- NSApp's already been initialized and
// its main run loop is already running.
[NSBundle loadNibFile:
[NSString stringWithUTF8String:(const char*)nibPath.get()]
externalNameTable:
@ -165,16 +188,26 @@ nsAppShell::Init()
forKey:@"NSOwner"]
withZone:NSDefaultMallocZone()];
// A message will be sent through mPort to mDelegate on the main thread
// to interrupt the run loop while it is running.
mDelegate = [[AppShellDelegate alloc] initWithAppShell:this];
NS_ENSURE_STATE(mDelegate);
mPort = [[NSPort port] retain];
NS_ENSURE_STATE(mPort);
// Add a CFRunLoopSource to the main native run loop. The source is
// responsible for interrupting the run loop when Gecko events are ready.
[mPort setDelegate:mDelegate];
[[NSRunLoop currentRunLoop] addPort:mPort forMode:NSDefaultRunLoopMode];
mCFRunLoop = [[NSRunLoop currentRunLoop] getCFRunLoop];
NS_ENSURE_STATE(mCFRunLoop);
::CFRetain(mCFRunLoop);
CFRunLoopSourceContext context;
bzero(&context, sizeof(context));
// context.version = 0;
context.info = this;
context.perform = ProcessGeckoEvents;
mCFRunLoopSource = ::CFRunLoopSourceCreate(kCFAllocatorDefault, 0, &context);
NS_ENSURE_STATE(mCFRunLoopSource);
::CFRunLoopAddSource(mCFRunLoop, mCFRunLoopSource, kCFRunLoopCommonModes);
rv = nsBaseAppShell::Init();
@ -185,25 +218,33 @@ nsAppShell::Init()
// ProcessGeckoEvents
//
// Arrange for Gecko events to be processed. They will either be processed
// after the main run loop returns (if we own the run loop) or on
// NativeEventCallback (if an embedder owns the loop).
// The "perform" target of mCFRunLoop, called when mCFRunLoopSource is
// signalled from ScheduleNativeEventCallback.
//
// Called by -[AppShellDelegate handlePortMessage:] after mPort signals as a
// result of a ScheduleNativeEventCallback call. This method is public only
// because it needs to be called by that Objective-C fragment, and C++ can't
// make |friend|s with Objective-C.
// Arrange for Gecko events to be processed on demand (in response to a call
// to ScheduleNativeEventCallback(), if processing of Gecko events via "native
// methods" hasn't been suspended). This happens in NativeEventCallback() ...
// or rather it's supposed to: nsBaseAppShell::NativeEventCallback() doesn't
// actually process any Gecko events if elsewhere we're also processing Gecko
// events in a tight loop (as happens in nsBaseAppShell::Run()) -- in that
// case ProcessGeckoEvents() is always called while ProcessNextNativeEvent()
// is running (called from nsBaseAppShell::OnProcessNextEvent()) and
// mProcessingNextNativeEvent is always true (which makes NativeEventCallback()
// take an early out).
//
// public
// protected static
void
nsAppShell::ProcessGeckoEvents()
nsAppShell::ProcessGeckoEvents(void* aInfo)
{
if (mRunningEventLoop) {
mRunningEventLoop = PR_FALSE;
nsAppShell* self = static_cast<nsAppShell*> (aInfo);
// The run loop is sleeping. [NSApp nextEventMatchingMask:...] won't
// return until it's given a reason to wake up. Awaken it by posting
// a bogus event. There's no need to make the event presentable.
if (self->mRunningEventLoop) {
self->mRunningEventLoop = PR_FALSE;
// The run loop may be sleeping -- [NSRunLoop acceptInputForMode:...]
// won't return until it's given a reason to wake up. Awaken it by
// posting a bogus event. There's no need to make the event
// presentable.
[NSApp postEvent:[NSEvent otherEventWithType:NSApplicationDefined
location:NSMakePoint(0,0)
modifierFlags:0
@ -216,12 +257,14 @@ nsAppShell::ProcessGeckoEvents()
atStart:NO];
}
if (mSuspendNativeCount <= 0) {
NativeEventCallback();
if (self->mSuspendNativeCount <= 0) {
self->NativeEventCallback();
} else {
mSkippedNativeCallback = PR_TRUE;
self->mSkippedNativeCallback = PR_TRUE;
}
// Still needed to fix bug 343033 ("5-10 second delay or hang or crash
// when quitting Cocoa Firefox").
[NSApp postEvent:[NSEvent otherEventWithType:NSApplicationDefined
location:NSMakePoint(0,0)
modifierFlags:0
@ -232,19 +275,41 @@ nsAppShell::ProcessGeckoEvents()
data1:0
data2:0]
atStart:NO];
// Each Release() here is balanced by exactly one AddRef() in
// ScheduleNativeEventCallback().
NS_RELEASE(self);
}
// WillTerminate
//
// Called by the AppShellDelegate when an NSApplicationWillTerminate
// notification is posted. After this method is called, native events should
// no longer be processed.
// no longer be processed. The NSApplicationWillTerminate notification is
// only posted when [NSApp terminate:] is called, which doesn't happen on a
// "normal" application quit.
//
// public
void
nsAppShell::WillTerminate()
{
if (mTerminated)
return;
mTerminated = PR_TRUE;
// Ugly hack to stop _NSAutoreleaseNoPool errors on shutdown from Camino --
// these seem to be triggered by our call here to NS_ProcessPendingEvents().
[[NSAutoreleasePool alloc] init];
// Calling [NSApp terminate:] causes (among other things) an
// NSApplicationWillTerminate notification to be posted and the main run
// loop to die before returning (in the call to [NSApp run]). So this is
// our last crack at processing any remaining Gecko events.
NS_ProcessPendingEvents(NS_GetCurrentThread());
// Unless we call nsBaseAppShell::Exit() here, it might not get called
// at all.
nsBaseAppShell::Exit();
}
// ScheduleNativeEventCallback
@ -253,26 +318,33 @@ nsAppShell::WillTerminate()
// needs to be processed. The Gecko event needs to be processed on the
// main thread, so the native run loop must be interrupted.
//
// In nsBaseAppShell.cpp, the mNativeEventPending variable is used to
// ensure that ScheduleNativeEventCallback() is called no more than once
// per call to NativeEventCallback(). ProcessGeckoEvents() can skip its
// call to NativeEventCallback() if processing of Gecko events by native
// means is suspended (using nsIAppShell::SuspendNative()), which will
// suspend calls from nsBaseAppShell::OnDispatchedEvent() to
// ScheduleNativeEventCallback(). But when Gecko event processing by
// native means is resumed (in ResumeNative()), an extra call is made to
// ScheduleNativeEventCallback() (from ResumeNative()). This triggers
// another call to ProcessGeckoEvents(), which calls NativeEventCallback(),
// and nsBaseAppShell::OnDispatchedEvent() resumes calling
// ScheduleNativeEventCallback().
//
// protected virtual
void
nsAppShell::ScheduleNativeEventCallback()
{
NS_ADDREF(this);
if (mTerminated)
return;
void* self = static_cast<void*>(this);
NSData* data = [[NSData alloc] initWithBytes:&self length:sizeof(this)];
NSArray* components = [[NSArray alloc] initWithObjects:&data count:1];
// Each AddRef() here is balanced by exactly one Release() in
// ProcessGeckoEvents().
NS_ADDREF_THIS();
// This will invoke [mDelegate handlePortMessage:message] on the main thread.
NSPortMessage* message = [[NSPortMessage alloc] initWithSendPort:mPort
receivePort:nil
components:components];
[message sendBeforeDate:[NSDate distantFuture]];
[message release];
[components release];
[data release];
// This will invoke ProcessGeckoEvents on the main thread.
::CFRunLoopSourceSignal(mCFRunLoopSource);
::CFRunLoopWakeUp(mCFRunLoop);
}
// ProcessNextNativeEvent
@ -282,14 +354,21 @@ nsAppShell::ScheduleNativeEventCallback()
//
// Returns true if more events are waiting in the native event queue.
//
// But (now that we're using [NSRunLoop acceptInputForMode:beforeDate:]) it's
// too expensive to call ProcessNextNativeEvent() many times in a row (in a
// tight loop), so we never return true more than kHadMoreEventsCountMax
// times in a row. This doesn't seem to cause native event starvation.
//
// protected virtual
PRBool
nsAppShell::ProcessNextNativeEvent(PRBool aMayWait)
{
PRBool moreEvents = PR_FALSE;
PRBool eventProcessed = PR_FALSE;
NSString* currentMode = nil;
if (mTerminated)
return eventProcessed;
return moreEvents;
PRBool wasRunningEventLoop = mRunningEventLoop;
mRunningEventLoop = aMayWait;
@ -303,56 +382,135 @@ nsAppShell::ProcessNextNativeEvent(PRBool aMayWait)
NS_ASSERTION(mAutoreleasePools && ::CFArrayGetCount(mAutoreleasePools),
"No autorelease pool for native event");
if (NSEvent* event = [NSApp nextEventMatchingMask:NSAnyEventMask
untilDate:waitUntil
inMode:NSDefaultRunLoopMode
dequeue:YES]) {
[NSApp sendEvent:event];
// If an event is waiting to be processed, run the main event loop
// just long enough to process it. For some reason, using [NSApp
// nextEventMatchingMask:...] to dequeue the event and [NSApp sendEvent:]
// to "send" it causes trouble, so we no longer do that. (The trouble
// was very strange, and only happened while processing Gecko events on
// demand (via ProcessGeckoEvents()), as opposed to processing Gecko
// events in a tight loop (via nsBaseAppShell::Run()): Particularly in
// Camino, mouse-down events sometimes got dropped (or mis-handled), so
// that (for example) you sometimes needed to click more than once on a
// button to make it work (the zoom button was particularly susceptible).
// You also sometimes had to ctrl-click or right-click multiple times to
// bring up a context menu.)
// Additional processing that [NSApp run] does after each event.
NSEventType type = [event type];
if (type != NSPeriodic && type != NSMouseMoved) {
[[NSApp servicesMenu] update];
[[NSApp windowsMenu] update];
[[NSApp mainMenu] update];
// Now that we're using [NSRunLoop acceptInputForMode:beforeDate:], it's
// too expensive to call ProcessNextNativeEvent() many times in a row, so
// we never return true more than kHadMoreEventsCountMax in a row. I'm
// not entirely sure why [NSRunLoop acceptInputForMode:beforeDate:] is too
// expensive, since it and its cousin [NSRunLoop runMode:beforeDate:] are
// designed to be called in a tight loop. Possibly the problem is due to
// combining [NSRunLoop acceptInputForMode:beforeDate] with [NSApp
// nextEventMatchingMask:...].
// If the current mode is something else than NSDefaultRunLoopMode, look
// for events in that mode.
currentMode = [[NSRunLoop currentRunLoop] currentMode];
if (!currentMode)
currentMode = NSDefaultRunLoopMode;
// If we're running modal (either Cocoa modal or XUL modal) we still need
// to use nextEventMatchingMask and sendEvent -- otherwise (in Minefield)
// the modal window won't receive key events or most mouse events.
if ([NSApp _isRunningModal] || (gXULModalLevel > 0)) {
if (NSEvent* event = [NSApp nextEventMatchingMask:NSAnyEventMask
untilDate:waitUntil
inMode:currentMode
dequeue:YES]) {
[NSApp sendEvent:event];
eventProcessed = PR_TRUE;
}
} else {
if (aMayWait ||
[NSApp nextEventMatchingMask:NSAnyEventMask
untilDate:nil
inMode:currentMode
dequeue:NO]) {
[[NSRunLoop currentRunLoop] acceptInputForMode:currentMode
beforeDate:waitUntil];
eventProcessed = PR_TRUE;
}
[NSApp updateWindows];
eventProcessed = PR_TRUE;
}
} while (mRunningEventLoop);
if (eventProcessed && (mHadMoreEventsCount < kHadMoreEventsCountMax)) {
moreEvents = ([NSApp nextEventMatchingMask:NSAnyEventMask
untilDate:nil
inMode:currentMode
dequeue:NO] != nil);
}
if (moreEvents) {
// Once this reaches kHadMoreEventsCountMax, it will be reset to 0 the
// next time through (whether or not we process any events then).
++mHadMoreEventsCount;
} else {
mHadMoreEventsCount = 0;
}
mRunningEventLoop = wasRunningEventLoop;
return eventProcessed;
return moreEvents;
}
// Run
//
// Overrides the base class' Run method to ensure that [NSApp run] has been
// called. When [NSApp run] has not yet been called, this method calls it
// after arranging for a selector to be called from the run loop. That
// selector is responsible for calling Run again. At that point, because
// [NSApp run] has been called, the base class' method is called.
// Overrides the base class's Run() method to call [NSApp run] (which spins
// the native run loop until the application quits). Since (unlike the base
// class's Run() method) we don't process any Gecko events here, they need
// to be processed elsewhere (in NativeEventCallback(), called from
// ProcessGeckoEvents()).
//
// The runAppShell selector will call [NSApp stop:] as soon as the real
// Run method finishes. The real Run method's return value is saved so
// that it may properly be returned.
// Camino calls [NSApp run] on its own (via NSApplicationMain()), and so
// doesn't call nsAppShell::Run().
//
// public
NS_IMETHODIMP
nsAppShell::Run(void)
{
if (![NSApp isRunning]) {
[mDelegate performSelector:@selector(runAppShell)
withObject:nil
afterDelay:0];
[NSApp run];
return [mDelegate rvFromRun];
}
NS_ASSERTION(!mStarted, "nsAppShell::Run() called multiple times");
if (mStarted)
return NS_OK;
return nsBaseAppShell::Run();
mStarted = PR_TRUE;
[NSApp run];
return NS_OK;
}
NS_IMETHODIMP
nsAppShell::Exit(void)
{
// This method is currently called more than once -- from (according to
// mento) an nsAppExitEvent dispatched by nsAppStartup::Quit() and from an
// XPCOM shutdown notification that nsBaseAppShell has registered to
// receive. So we need to ensure that multiple calls won't break anything.
// But we should also complain about it (since it isn't quite kosher).
NS_ASSERTION(!mTerminated, "nsAppShell::Exit() called redundantly");
if (mTerminated)
return NS_OK;
mTerminated = PR_TRUE;
// Quoting from Apple's doc on the [NSApplication stop:] method (from their
// doc on the NSApplication class): "If this method is invoked during a
// modal event loop, it will break that loop but not the main event loop."
// nsAppShell::Exit() shouldn't be called from a modal event loop. So if
// it is we complain about it (to users of debug builds) and call [NSApp
// stop:] one extra time. (I'm not sure if modal event loops can be nested
// -- Apple's docs don't say one way or the other. But the return value
// of [NSApp _isRunningModal] doesn't change immediately after a call to
// [NSApp stop:], so we have to assume that one extra call to [NSApp stop:]
// will do the job.)
BOOL cocoaModal = [NSApp _isRunningModal];
NS_ASSERTION(!cocoaModal,
"Don't call nsAppShell::Exit() from a modal event loop!");
if (cocoaModal)
[NSApp stop:nsnull];
[NSApp stop:nsnull];
return nsBaseAppShell::Exit();
}
// OnProcessNextEvent
@ -413,7 +571,6 @@ nsAppShell::AfterProcessNextEvent(nsIThreadInternal *aThread,
{
if ((self = [self init])) {
mAppShell = aAppShell;
mRunRV = NS_ERROR_NOT_INITIALIZED;
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(applicationWillTerminate:)
@ -435,40 +592,6 @@ nsAppShell::AfterProcessNextEvent(nsIThreadInternal *aThread,
[super dealloc];
}
// handlePortMessage:
//
// The selector called on the delegate object when nsAppShell::mPort is sent an
// NSPortMessage by ScheduleNativeEventCallback. Call into the nsAppShell
// object for access to mRunningEventLoop and NativeEventCallback.
//
- (void)handlePortMessage:(NSPortMessage*)aPortMessage
{
NSData* data = [[aPortMessage components] objectAtIndex:0];
nsAppShell* appShell = *static_cast<nsAppShell* const*>([data bytes]);
appShell->ProcessGeckoEvents();
NS_RELEASE(appShell);
}
// runAppShell
//
// Runs the nsAppShell, and immediately stops the Cocoa run loop when
// nsAppShell::Run is done, saving its return value.
- (void)runAppShell
{
mRunRV = mAppShell->Run();
[NSApp stop:self];
return;
}
// rvFromRun
//
// Returns the nsresult return value saved by runAppShell.
- (nsresult)rvFromRun
{
return mRunRV;
}
// applicationWillTerminate:
//
// Notify the nsAppShell that native event processing should be discontinued.
@ -492,3 +615,4 @@ nsAppShell::AfterProcessNextEvent(nsIThreadInternal *aThread,
}
@end

View File

@ -152,7 +152,7 @@ public:
NS_IMETHOD AddMenuListener(nsIMenuListener * aListener);
NS_IMETHOD Enable(PRBool aState);
NS_IMETHOD IsEnabled(PRBool *aState);
NS_IMETHOD SetModal(PRBool aState) { return NS_OK; }
NS_IMETHOD SetModal(PRBool aState);
NS_IMETHOD IsVisible(PRBool & aState);
NS_IMETHOD SetFocus(PRBool aState=PR_FALSE);
NS_IMETHOD SetMenuBar(nsIMenuBar * aMenuBar);
@ -217,6 +217,7 @@ protected:
PRPackedBool mWindowMadeHere; // true if we created the window, false for embedding
PRPackedBool mVisible; // Whether or not we're visible.
PRPackedBool mSheetNeedsShow; // if this is a sheet, are we waiting to be shown?
PRPackedBool mModal;
};

View File

@ -52,6 +52,8 @@
#include "nsIPrefService.h"
#include "nsIPrefBranch.h"
PRInt32 gXULModalLevel = 0;
// defined in nsMenuBarX.mm
extern NSMenu* sApplicationMenu; // Application menu shared by all menubars
@ -89,6 +91,7 @@ nsCocoaWindow::nsCocoaWindow()
, mWindowMadeHere(PR_FALSE)
, mVisible(PR_FALSE)
, mSheetNeedsShow(PR_FALSE)
, mModal(PR_FALSE)
{
}
@ -111,6 +114,13 @@ nsCocoaWindow::~nsCocoaWindow()
}
NS_IF_RELEASE(mPopupContentView);
// Deal with the possiblity that we're being destroyed while running modal.
NS_ASSERTION(!mModal, "Widget destroyed while running modal!");
if (mModal) {
--gXULModalLevel;
NS_ASSERTION(gXULModalLevel >= 0, "Wierdness setting modality!");
}
}
@ -425,6 +435,19 @@ NS_IMETHODIMP nsCocoaWindow::IsVisible(PRBool & aState)
}
NS_IMETHODIMP nsCocoaWindow::SetModal(PRBool aState)
{
mModal = aState;
if (aState) {
++gXULModalLevel;
} else {
--gXULModalLevel;
NS_ASSERTION(gXULModalLevel >= 0, "Mismatched call to nsCocoaWindow::SetModal(PR_FALSE)!");
}
return NS_OK;
}
// Hide or show this window
NS_IMETHODIMP nsCocoaWindow::Show(PRBool bState)
{

View File

@ -217,11 +217,18 @@
- (void)windowWillClose:(NSNotification*)inNotification
{
// We can get _NSAutoreleaseNoPool errors on shutdown here (from Camino)
// unless we use a local pool -- possibly triggered by a call we're now
// making to NS_ProcessPendingEvents() from nsAppShell::WillTerminate().
NSAutoreleasePool* localPool = [[NSAutoreleasePool alloc] init];
// postpone our destruction
[[self retain] autorelease];
// remove ourselves from the window map (which owns us)
[[WindowDataMap sharedWindowDataMap] removeDataForWindow:[inNotification object]];
[localPool release];
}
@end