Index: base/message_pump_mac.mm |
=================================================================== |
--- base/message_pump_mac.mm (revision 31104) |
+++ base/message_pump_mac.mm (working copy) |
@@ -11,7 +11,8 @@ |
#include <limits> |
-#include "base/scoped_nsautorelease_pool.h" |
+#import "base/chrome_application_mac.h" |
+#include "base/logging.h" |
#include "base/time.h" |
namespace { |
@@ -26,6 +27,23 @@ |
namespace base { |
+// A scoper for autorelease pools created from message pump run loops. |
+// Avoids dirtying up the ScopedNSAutoreleasePool interface for the rare |
+// case where an autorelease pool needs to be passed in. |
+class MessagePumpScopedAutoreleasePool { |
+ public: |
+ explicit MessagePumpScopedAutoreleasePool(MessagePumpCFRunLoopBase* pump) : |
+ pool_(pump->CreateAutoreleasePool()) { |
Mark Mentovai
2009/11/05 20:28:40
Final really tiny minor insignificant nit that you
|
+ } |
+ ~MessagePumpScopedAutoreleasePool() { |
Scott Hess - ex-Googler
2009/11/05 20:36:12
Nit: indentation.
|
+ [pool_ drain]; |
+ } |
+ |
+ private: |
+ NSAutoreleasePool* pool_; |
+ DISALLOW_COPY_AND_ASSIGN(MessagePumpScopedAutoreleasePool); |
+}; |
+ |
// Must be called on the run loop thread. |
MessagePumpCFRunLoopBase::MessagePumpCFRunLoopBase() |
: delegate_(NULL), |
@@ -264,9 +282,9 @@ |
// The NSApplication-based run loop only drains the autorelease pool at each |
// UI event (NSEvent). The autorelease pool is not drained for each |
// CFRunLoopSource target that's run. Use a local pool for any autoreleased |
- // objects to ensure they're released promptly even in the absence of UI |
- // events. |
- ScopedNSAutoreleasePool autorelease_pool; |
+ // objects if the app is not currently handling a UI event to ensure they're |
+ // released promptly even in the absence of UI events. |
+ MessagePumpScopedAutoreleasePool autorelease_pool(this); |
// Call DoWork once, and if something was done, arrange to come back here |
// again as long as the loop is still running. |
@@ -298,9 +316,9 @@ |
// The NSApplication-based run loop only drains the autorelease pool at each |
// UI event (NSEvent). The autorelease pool is not drained for each |
// CFRunLoopSource target that's run. Use a local pool for any autoreleased |
- // objects to ensure they're released promptly even in the absence of UI |
- // events. |
- ScopedNSAutoreleasePool autorelease_pool; |
+ // objects if the app is not currently handling a UI event to ensure they're |
+ // released promptly even in the absence of UI events. |
+ MessagePumpScopedAutoreleasePool autorelease_pool(this); |
Time next_time; |
delegate_->DoDelayedWork(&next_time); |
@@ -342,9 +360,9 @@ |
// The NSApplication-based run loop only drains the autorelease pool at each |
// UI event (NSEvent). The autorelease pool is not drained for each |
// CFRunLoopSource target that's run. Use a local pool for any autoreleased |
- // objects to ensure they're released promptly even in the absence of UI |
- // events. |
- ScopedNSAutoreleasePool autorelease_pool; |
+ // objects if the app is not currently handling a UI event to ensure they're |
+ // released promptly even in the absence of UI events. |
+ MessagePumpScopedAutoreleasePool autorelease_pool(this); |
// Call DoIdleWork once, and if something was done, arrange to come back here |
// again as long as the loop is still running. |
@@ -555,6 +573,11 @@ |
void MessagePumpCFRunLoopBase::EnterExitRunLoop(CFRunLoopActivity activity) { |
} |
+// Base version returns a standard NSAutoreleasePool. |
+NSAutoreleasePool* MessagePumpCFRunLoopBase::CreateAutoreleasePool() { |
+ return [[NSAutoreleasePool alloc] init]; |
+} |
+ |
MessagePumpCFRunLoop::MessagePumpCFRunLoop() |
: quit_pending_(false) { |
} |
@@ -568,7 +591,7 @@ |
// pool management is introduced. |
int result; |
do { |
- ScopedNSAutoreleasePool autorelease_pool; |
+ MessagePumpScopedAutoreleasePool autorelease_pool(this); |
result = CFRunLoopRunInMode(kCFRunLoopDefaultMode, |
kCFTimeIntervalMax, |
false); |
@@ -644,7 +667,9 @@ |
void MessagePumpNSApplication::DoRun(Delegate* delegate) { |
bool last_running_own_loop_ = running_own_loop_; |
- [NSApplication sharedApplication]; |
+ // TODO(dmaclach): Get rid of this gratuitous sharedApplication. |
+ // Tests should be setting up their applications on their own. |
+ [CrApplication sharedApplication]; |
if (![NSApp isRunning]) { |
running_own_loop_ = false; |
@@ -654,7 +679,7 @@ |
running_own_loop_ = true; |
NSDate* distant_future = [NSDate distantFuture]; |
while (keep_running_) { |
- ScopedNSAutoreleasePool autorelease_pool; |
+ MessagePumpScopedAutoreleasePool autorelease_pool(this); |
NSEvent* event = [NSApp nextEventMatchingMask:NSAnyEventMask |
untilDate:distant_future |
inMode:NSDefaultRunLoopMode |
@@ -689,6 +714,46 @@ |
atStart:NO]; |
} |
+// Prevents an autorelease pool from being created if the app is in the midst of |
+// handling a UI event because various parts of AppKit depend on objects that |
+// are created while handling a UI event to be autoreleased in the event loop. |
+// An example of this is NSWindowController. When a window with a window |
+// controller is closed it goes through a stack like this: |
+// (Several stack frames elided for clarity) |
+// |
+// #0 [NSWindowController autorelease] |
+// #1 DoAClose |
+// #2 MessagePumpCFRunLoopBase::DoWork() |
+// #3 [NSRunLoop run] |
+// #4 [NSButton performClick:] |
+// #5 [NSWindow sendEvent:] |
+// #6 [NSApp sendEvent:] |
+// #7 [NSApp run] |
+// |
+// -performClick: spins a nested run loop. If the pool created in DoWork was a |
+// standard NSAutoreleasePool, it would release the objects that were |
+// autoreleased into it once DoWork released it. This would cause the window |
+// controller, which autoreleased itself in frame #0, to release itself, and |
+// possibly free itself. Unfortunately this window controller controls the |
+// window in frame #5. When the stack is unwound to frame #5, the window would |
+// no longer exists and crashes may occur. Apple gets around this by never |
+// releasing the pool it creates in frame #4, and letting frame #7 clean it up |
+// when it cleans up the pool that wraps frame #7. When an autorelease pool is |
+// released it releases all other pools that were created after it on the |
+// autorelease pool stack. |
+// |
+// CrApplication is responsible for setting handlingSendEvent to true just |
+// before it sends the event throught the event handling mechanism, and |
+// returning it to its previous value once the event has been sent. |
+NSAutoreleasePool* MessagePumpNSApplication::CreateAutoreleasePool() { |
+ NSAutoreleasePool* pool = nil; |
+ DCHECK([NSApp isKindOfClass:[CrApplication class]]); |
+ if (![static_cast<CrApplication*>(NSApp) isHandlingSendEvent]) { |
+ pool = MessagePumpCFRunLoopBase::CreateAutoreleasePool(); |
+ } |
+ return pool; |
+} |
+ |
// static |
MessagePump* MessagePumpMac::Create() { |
if ([NSThread isMainThread]) { |