diff --git a/widget/src/cocoa/nsAppShell.mm b/widget/src/cocoa/nsAppShell.mm index 7b08d4e1dfb..001c5a32df5 100644 --- a/widget/src/cocoa/nsAppShell.mm +++ b/widget/src/cocoa/nsAppShell.mm @@ -54,6 +54,7 @@ #include "nsServiceManagerUtils.h" #include "nsIInterfaceRequestor.h" #include "nsIWebBrowserChrome.h" +#include "nsObjCExceptions.h" // defined in nsChildView.mm extern nsIRollupListener * gRollupListener; @@ -125,6 +126,8 @@ nsAppShell::nsAppShell() nsAppShell::~nsAppShell() { + NS_OBJC_BEGIN_TRY_ABORT_BLOCK; + if (mCFRunLoop) { if (mCFRunLoopSource) { ::CFRunLoopRemoveSource(mCFRunLoop, mCFRunLoopSource, @@ -163,6 +166,8 @@ nsAppShell::~nsAppShell() // on the current thread, which is the main thread). if (!mNotifiedWillTerminate) [mMainPool release]; + + NS_OBJC_END_TRY_ABORT_BLOCK } // Init @@ -174,6 +179,8 @@ nsAppShell::~nsAppShell() nsresult nsAppShell::Init() { + NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; + // 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 @@ -238,6 +245,8 @@ nsAppShell::Init() [localPool release]; return rv; + + NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; } // ProcessGeckoEvents @@ -260,6 +269,8 @@ nsAppShell::Init() void nsAppShell::ProcessGeckoEvents(void* aInfo) { + NS_OBJC_BEGIN_TRY_ABORT_BLOCK; + nsAppShell* self = static_cast (aInfo); if (self->mRunningEventLoop) { @@ -313,6 +324,8 @@ nsAppShell::ProcessGeckoEvents(void* aInfo) // Each Release() here is balanced by exactly one AddRef() in // ScheduleNativeEventCallback(). NS_RELEASE(self); + + NS_OBJC_END_TRY_ABORT_BLOCK; } // WillTerminate @@ -366,6 +379,8 @@ nsAppShell::WillTerminate() void nsAppShell::ScheduleNativeEventCallback() { + NS_OBJC_BEGIN_TRY_ABORT_BLOCK; + if (mTerminated) return; @@ -376,6 +391,8 @@ nsAppShell::ScheduleNativeEventCallback() // This will invoke ProcessGeckoEvents on the main thread. ::CFRunLoopSourceSignal(mCFRunLoopSource); ::CFRunLoopWakeUp(mCFRunLoop); + + NS_OBJC_END_TRY_ABORT_BLOCK; } // ProcessNextNativeEvent @@ -395,6 +412,9 @@ PRBool nsAppShell::ProcessNextNativeEvent(PRBool aMayWait) { PRBool moreEvents = PR_FALSE; + + NS_OBJC_BEGIN_TRY_ABORT_BLOCK; + PRBool eventProcessed = PR_FALSE; NSString* currentMode = nil; @@ -510,6 +530,8 @@ nsAppShell::ProcessNextNativeEvent(PRBool aMayWait) mRunningEventLoop = wasRunningEventLoop; + NS_OBJC_END_TRY_ABORT_BLOCK; + return moreEvents; } @@ -562,7 +584,7 @@ nsAppShell::Run(void) return NS_OK; mStarted = PR_TRUE; - [NSApp run]; + NS_OBJC_TRY_ABORT([NSApp run]); return NS_OK; } @@ -570,6 +592,8 @@ nsAppShell::Run(void) NS_IMETHODIMP nsAppShell::Exit(void) { + NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; + // 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 @@ -600,6 +624,8 @@ nsAppShell::Exit(void) [NSApp stop:nsnull]; return nsBaseAppShell::Exit(); + + NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; } // OnProcessNextEvent @@ -616,6 +642,8 @@ NS_IMETHODIMP nsAppShell::OnProcessNextEvent(nsIThreadInternal *aThread, PRBool aMayWait, PRUint32 aRecursionDepth) { + NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; + mRecursionDepth = aRecursionDepth; NS_ASSERTION(mAutoreleasePools, @@ -625,6 +653,8 @@ nsAppShell::OnProcessNextEvent(nsIThreadInternal *aThread, PRBool aMayWait, ::CFArrayAppendValue(mAutoreleasePools, pool); return nsBaseAppShell::OnProcessNextEvent(aThread, aMayWait, aRecursionDepth); + + NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; } // AfterProcessNextEvent @@ -638,6 +668,8 @@ NS_IMETHODIMP nsAppShell::AfterProcessNextEvent(nsIThreadInternal *aThread, PRUint32 aRecursionDepth) { + NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; + mRecursionDepth = aRecursionDepth; CFIndex count = ::CFArrayGetCount(mAutoreleasePools); @@ -651,6 +683,8 @@ nsAppShell::AfterProcessNextEvent(nsIThreadInternal *aThread, [pool release]; return nsBaseAppShell::AfterProcessNextEvent(aThread, aRecursionDepth); + + NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; } // AppShellDelegate implementation @@ -661,6 +695,8 @@ nsAppShell::AfterProcessNextEvent(nsIThreadInternal *aThread, // Constructs the AppShellDelegate object - (id)initWithAppShell:(nsAppShell*)aAppShell { + NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; + if ((self = [self init])) { mAppShell = aAppShell; @@ -675,13 +711,19 @@ nsAppShell::AfterProcessNextEvent(nsIThreadInternal *aThread, } return self; + + NS_OBJC_END_TRY_ABORT_BLOCK_NIL; } - (void)dealloc { + NS_OBJC_BEGIN_TRY_ABORT_BLOCK; + [[NSNotificationCenter defaultCenter] removeObserver:self]; [[NSDistributedNotificationCenter defaultCenter] removeObserver:self]; [super dealloc]; + + NS_OBJC_END_TRY_ABORT_BLOCK; } // applicationWillTerminate: @@ -689,7 +731,11 @@ nsAppShell::AfterProcessNextEvent(nsIThreadInternal *aThread, // Notify the nsAppShell that native event processing should be discontinued. - (void)applicationWillTerminate:(NSNotification*)aNotification { + NS_OBJC_BEGIN_TRY_ABORT_BLOCK; + mAppShell->WillTerminate(); + + NS_OBJC_END_TRY_ABORT_BLOCK; } // beginMenuTracking @@ -699,12 +745,15 @@ nsAppShell::AfterProcessNextEvent(nsIThreadInternal *aThread, // send ourselves (whose 'sender' will be @"org.mozilla.gecko.PopupWindow"). - (void)beginMenuTracking:(NSNotification*)aNotification { + NS_OBJC_BEGIN_TRY_ABORT_BLOCK; + NSString *sender = [aNotification object]; if (!sender || ![sender isEqualToString:@"org.mozilla.gecko.PopupWindow"]) { if (gRollupListener && gRollupWidget) gRollupListener->Rollup(nsnull); } + + NS_OBJC_END_TRY_ABORT_BLOCK; } @end - diff --git a/xpcom/base/Makefile.in b/xpcom/base/Makefile.in index 78ffbd01053..ea8ef1fc311 100644 --- a/xpcom/base/Makefile.in +++ b/xpcom/base/Makefile.in @@ -114,6 +114,7 @@ SDK_HEADERS = \ nsISupportsBase.h \ nscore.h \ nsCycleCollector.h \ + nsObjCExceptions.h \ XPIDLSRCS = \ nsIConsoleListener.idl \ diff --git a/xpcom/base/nsObjCExceptions.h b/xpcom/base/nsObjCExceptions.h new file mode 100644 index 00000000000..1fe620cc2f8 --- /dev/null +++ b/xpcom/base/nsObjCExceptions.h @@ -0,0 +1,137 @@ +/* -*- 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 + * Mozilla Corporation. + * Portions created by the Initial Developer are Copyright (C) 2008 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Josh Aas + * Robert O'Callahan + * + * 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 NPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef nsObjCExceptions_h_ +#define nsObjCExceptions_h_ + +#import +#import +#include +#include +#include "nsError.h" + +// See Mozilla bug 163260. +// This file can only be included in an Objective-C context. + +static void nsObjCExceptionLog(NSException *e) +{ + NSLog(@"%@: %@", [e name], [e reason]); +} + +static void nsObjCExceptionAbort() +{ + // We need to raise a mach-o signal here, the Mozilla crash reporter on + // Mac OS X does not respond to POSIX signals. Raising mach-o signals directly + // is tricky so we do it by just derefing a null pointer. + int* foo = NULL; + *foo = 1; +} + +static void nsObjCExceptionLogAbort(NSException *e) +{ + nsObjCExceptionLog(e); + nsObjCExceptionAbort(); +} + +#define NS_OBJC_TRY(_e, _fail) \ +@try { _e; } \ +@catch(NSException *_exn) { \ + nsObjCExceptionLog(_exn); \ + _fail; \ +} + +#define NS_OBJC_TRY_EXPR(_e, _fail) \ +({ \ + typeof(_e) _tmp; \ + @try { _tmp = (_e); } \ + @catch(NSException *_exn) { \ + nsObjCExceptionLog(_exn); \ + _fail; \ + } \ + _tmp; \ +}) + +#define NS_OBJC_TRY_EXPR_NULL(_e) \ +NS_OBJC_TRY_EXPR(_e, 0) + +#define NS_OBJC_TRY_IGNORE(_e) \ +NS_OBJC_TRY(_e, ) + +// To reduce code size the abort versions do not reuse above macros. This +// allows catch blocks to only contain one call. + +#define NS_OBJC_TRY_ABORT(_e) \ +@try { _e; } \ +@catch(NSException *_exn) { \ + nsObjCExceptionLogAbort(_exn); \ +} + +#define NS_OBJC_TRY_EXPR_ABORT(_e) \ +({ \ + typeof(_e) _tmp; \ + @try { _tmp = (_e); } \ + @catch(NSException *_exn) { \ + nsObjCExceptionLogAbort(_exn); \ + } \ + _tmp; \ +}) + +// For wrapping blocks of Obj-C calls. Terminates app after logging. +#define NS_OBJC_BEGIN_TRY_ABORT_BLOCK @try { +#define NS_OBJC_END_TRY_ABORT_BLOCK } @catch(NSException *_exn) { \ + nsObjCExceptionLogAbort(_exn); \ + } + +// Same as above ABORT_BLOCK but returns a value after the try/catch block to +// suppress compiler warnings. This allows us to avoid having to refactor code +// to get scoping right when wrapping an entire method. + +#define NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL @try { +#define NS_OBJC_END_TRY_ABORT_BLOCK_NIL } @catch(NSException *_exn) { \ + nsObjCExceptionLogAbort(_exn); \ + } \ + return nil; + +#define NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT @try { +#define NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT } @catch(NSException *_exn) { \ + nsObjCExceptionLogAbort(_exn);\ + } \ + return NS_ERROR_FAILURE; + +#endif // nsObjCExceptions_h_