1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #import "base/message_loop/message_pump_mac.h" 6 7 #import <Foundation/Foundation.h> 8 9 #include <limits> 10 11 #include "base/logging.h" 12 #include "base/run_loop.h" 13 #include "base/time/time.h" 14 15 #if !defined(OS_IOS) 16 #import <AppKit/AppKit.h> 17 #endif // !defined(OS_IOS) 18 19 namespace { 20 21 void NoOp(void* info) { 22 } 23 24 const CFTimeInterval kCFTimeIntervalMax = 25 std::numeric_limits<CFTimeInterval>::max(); 26 27 #if !defined(OS_IOS) 28 // Set to true if MessagePumpMac::Create() is called before NSApp is 29 // initialized. Only accessed from the main thread. 30 bool g_not_using_cr_app = false; 31 #endif 32 33 } // namespace 34 35 namespace base { 36 37 // A scoper for autorelease pools created from message pump run loops. 38 // Avoids dirtying up the ScopedNSAutoreleasePool interface for the rare 39 // case where an autorelease pool needs to be passed in. 40 class MessagePumpScopedAutoreleasePool { 41 public: 42 explicit MessagePumpScopedAutoreleasePool(MessagePumpCFRunLoopBase* pump) : 43 pool_(pump->CreateAutoreleasePool()) { 44 } 45 ~MessagePumpScopedAutoreleasePool() { 46 [pool_ drain]; 47 } 48 49 private: 50 NSAutoreleasePool* pool_; 51 DISALLOW_COPY_AND_ASSIGN(MessagePumpScopedAutoreleasePool); 52 }; 53 54 // Must be called on the run loop thread. 55 MessagePumpCFRunLoopBase::MessagePumpCFRunLoopBase() 56 : delegate_(NULL), 57 delayed_work_fire_time_(kCFTimeIntervalMax), 58 nesting_level_(0), 59 run_nesting_level_(0), 60 deepest_nesting_level_(0), 61 delegateless_work_(false), 62 delegateless_idle_work_(false) { 63 run_loop_ = CFRunLoopGetCurrent(); 64 CFRetain(run_loop_); 65 66 // Set a repeating timer with a preposterous firing time and interval. The 67 // timer will effectively never fire as-is. The firing time will be adjusted 68 // as needed when ScheduleDelayedWork is called. 69 CFRunLoopTimerContext timer_context = CFRunLoopTimerContext(); 70 timer_context.info = this; 71 delayed_work_timer_ = CFRunLoopTimerCreate(NULL, // allocator 72 kCFTimeIntervalMax, // fire time 73 kCFTimeIntervalMax, // interval 74 0, // flags 75 0, // priority 76 RunDelayedWorkTimer, 77 &timer_context); 78 CFRunLoopAddTimer(run_loop_, delayed_work_timer_, kCFRunLoopCommonModes); 79 80 CFRunLoopSourceContext source_context = CFRunLoopSourceContext(); 81 source_context.info = this; 82 source_context.perform = RunWorkSource; 83 work_source_ = CFRunLoopSourceCreate(NULL, // allocator 84 1, // priority 85 &source_context); 86 CFRunLoopAddSource(run_loop_, work_source_, kCFRunLoopCommonModes); 87 88 source_context.perform = RunIdleWorkSource; 89 idle_work_source_ = CFRunLoopSourceCreate(NULL, // allocator 90 2, // priority 91 &source_context); 92 CFRunLoopAddSource(run_loop_, idle_work_source_, kCFRunLoopCommonModes); 93 94 source_context.perform = RunNestingDeferredWorkSource; 95 nesting_deferred_work_source_ = CFRunLoopSourceCreate(NULL, // allocator 96 0, // priority 97 &source_context); 98 CFRunLoopAddSource(run_loop_, nesting_deferred_work_source_, 99 kCFRunLoopCommonModes); 100 101 CFRunLoopObserverContext observer_context = CFRunLoopObserverContext(); 102 observer_context.info = this; 103 pre_wait_observer_ = CFRunLoopObserverCreate(NULL, // allocator 104 kCFRunLoopBeforeWaiting, 105 true, // repeat 106 0, // priority 107 PreWaitObserver, 108 &observer_context); 109 CFRunLoopAddObserver(run_loop_, pre_wait_observer_, kCFRunLoopCommonModes); 110 111 pre_source_observer_ = CFRunLoopObserverCreate(NULL, // allocator 112 kCFRunLoopBeforeSources, 113 true, // repeat 114 0, // priority 115 PreSourceObserver, 116 &observer_context); 117 CFRunLoopAddObserver(run_loop_, pre_source_observer_, kCFRunLoopCommonModes); 118 119 enter_exit_observer_ = CFRunLoopObserverCreate(NULL, // allocator 120 kCFRunLoopEntry | 121 kCFRunLoopExit, 122 true, // repeat 123 0, // priority 124 EnterExitObserver, 125 &observer_context); 126 CFRunLoopAddObserver(run_loop_, enter_exit_observer_, kCFRunLoopCommonModes); 127 } 128 129 // Ideally called on the run loop thread. If other run loops were running 130 // lower on the run loop thread's stack when this object was created, the 131 // same number of run loops must be running when this object is destroyed. 132 MessagePumpCFRunLoopBase::~MessagePumpCFRunLoopBase() { 133 CFRunLoopRemoveObserver(run_loop_, enter_exit_observer_, 134 kCFRunLoopCommonModes); 135 CFRelease(enter_exit_observer_); 136 137 CFRunLoopRemoveObserver(run_loop_, pre_source_observer_, 138 kCFRunLoopCommonModes); 139 CFRelease(pre_source_observer_); 140 141 CFRunLoopRemoveObserver(run_loop_, pre_wait_observer_, 142 kCFRunLoopCommonModes); 143 CFRelease(pre_wait_observer_); 144 145 CFRunLoopRemoveSource(run_loop_, nesting_deferred_work_source_, 146 kCFRunLoopCommonModes); 147 CFRelease(nesting_deferred_work_source_); 148 149 CFRunLoopRemoveSource(run_loop_, idle_work_source_, kCFRunLoopCommonModes); 150 CFRelease(idle_work_source_); 151 152 CFRunLoopRemoveSource(run_loop_, work_source_, kCFRunLoopCommonModes); 153 CFRelease(work_source_); 154 155 CFRunLoopRemoveTimer(run_loop_, delayed_work_timer_, kCFRunLoopCommonModes); 156 CFRelease(delayed_work_timer_); 157 158 CFRelease(run_loop_); 159 } 160 161 // Must be called on the run loop thread. 162 void MessagePumpCFRunLoopBase::Run(Delegate* delegate) { 163 // nesting_level_ will be incremented in EnterExitRunLoop, so set 164 // run_nesting_level_ accordingly. 165 int last_run_nesting_level = run_nesting_level_; 166 run_nesting_level_ = nesting_level_ + 1; 167 168 Delegate* last_delegate = delegate_; 169 SetDelegate(delegate); 170 171 DoRun(delegate); 172 173 // Restore the previous state of the object. 174 SetDelegate(last_delegate); 175 run_nesting_level_ = last_run_nesting_level; 176 } 177 178 void MessagePumpCFRunLoopBase::SetDelegate(Delegate* delegate) { 179 delegate_ = delegate; 180 181 if (delegate) { 182 // If any work showed up but could not be dispatched for want of a 183 // delegate, set it up for dispatch again now that a delegate is 184 // available. 185 if (delegateless_work_) { 186 CFRunLoopSourceSignal(work_source_); 187 delegateless_work_ = false; 188 } 189 if (delegateless_idle_work_) { 190 CFRunLoopSourceSignal(idle_work_source_); 191 delegateless_idle_work_ = false; 192 } 193 } 194 } 195 196 // May be called on any thread. 197 void MessagePumpCFRunLoopBase::ScheduleWork() { 198 CFRunLoopSourceSignal(work_source_); 199 CFRunLoopWakeUp(run_loop_); 200 } 201 202 // Must be called on the run loop thread. 203 void MessagePumpCFRunLoopBase::ScheduleDelayedWork( 204 const TimeTicks& delayed_work_time) { 205 TimeDelta delta = delayed_work_time - TimeTicks::Now(); 206 delayed_work_fire_time_ = CFAbsoluteTimeGetCurrent() + delta.InSecondsF(); 207 CFRunLoopTimerSetNextFireDate(delayed_work_timer_, delayed_work_fire_time_); 208 } 209 210 // Called from the run loop. 211 // static 212 void MessagePumpCFRunLoopBase::RunDelayedWorkTimer(CFRunLoopTimerRef timer, 213 void* info) { 214 MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info); 215 216 // The timer won't fire again until it's reset. 217 self->delayed_work_fire_time_ = kCFTimeIntervalMax; 218 219 // CFRunLoopTimers fire outside of the priority scheme for CFRunLoopSources. 220 // In order to establish the proper priority in which work and delayed work 221 // are processed one for one, the timer used to schedule delayed work must 222 // signal a CFRunLoopSource used to dispatch both work and delayed work. 223 CFRunLoopSourceSignal(self->work_source_); 224 } 225 226 // Called from the run loop. 227 // static 228 void MessagePumpCFRunLoopBase::RunWorkSource(void* info) { 229 MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info); 230 self->RunWork(); 231 } 232 233 // Called by MessagePumpCFRunLoopBase::RunWorkSource. 234 bool MessagePumpCFRunLoopBase::RunWork() { 235 if (!delegate_) { 236 // This point can be reached with a NULL delegate_ if Run is not on the 237 // stack but foreign code is spinning the CFRunLoop. Arrange to come back 238 // here when a delegate is available. 239 delegateless_work_ = true; 240 return false; 241 } 242 243 // The NSApplication-based run loop only drains the autorelease pool at each 244 // UI event (NSEvent). The autorelease pool is not drained for each 245 // CFRunLoopSource target that's run. Use a local pool for any autoreleased 246 // objects if the app is not currently handling a UI event to ensure they're 247 // released promptly even in the absence of UI events. 248 MessagePumpScopedAutoreleasePool autorelease_pool(this); 249 250 // Call DoWork and DoDelayedWork once, and if something was done, arrange to 251 // come back here again as long as the loop is still running. 252 bool did_work = delegate_->DoWork(); 253 bool resignal_work_source = did_work; 254 255 TimeTicks next_time; 256 delegate_->DoDelayedWork(&next_time); 257 if (!did_work) { 258 // Determine whether there's more delayed work, and if so, if it needs to 259 // be done at some point in the future or if it's already time to do it. 260 // Only do these checks if did_work is false. If did_work is true, this 261 // function, and therefore any additional delayed work, will get another 262 // chance to run before the loop goes to sleep. 263 bool more_delayed_work = !next_time.is_null(); 264 if (more_delayed_work) { 265 TimeDelta delay = next_time - TimeTicks::Now(); 266 if (delay > TimeDelta()) { 267 // There's more delayed work to be done in the future. 268 ScheduleDelayedWork(next_time); 269 } else { 270 // There's more delayed work to be done, and its time is in the past. 271 // Arrange to come back here directly as long as the loop is still 272 // running. 273 resignal_work_source = true; 274 } 275 } 276 } 277 278 if (resignal_work_source) { 279 CFRunLoopSourceSignal(work_source_); 280 } 281 282 return resignal_work_source; 283 } 284 285 // Called from the run loop. 286 // static 287 void MessagePumpCFRunLoopBase::RunIdleWorkSource(void* info) { 288 MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info); 289 self->RunIdleWork(); 290 } 291 292 // Called by MessagePumpCFRunLoopBase::RunIdleWorkSource. 293 bool MessagePumpCFRunLoopBase::RunIdleWork() { 294 if (!delegate_) { 295 // This point can be reached with a NULL delegate_ if Run is not on the 296 // stack but foreign code is spinning the CFRunLoop. Arrange to come back 297 // here when a delegate is available. 298 delegateless_idle_work_ = true; 299 return false; 300 } 301 302 // The NSApplication-based run loop only drains the autorelease pool at each 303 // UI event (NSEvent). The autorelease pool is not drained for each 304 // CFRunLoopSource target that's run. Use a local pool for any autoreleased 305 // objects if the app is not currently handling a UI event to ensure they're 306 // released promptly even in the absence of UI events. 307 MessagePumpScopedAutoreleasePool autorelease_pool(this); 308 309 // Call DoIdleWork once, and if something was done, arrange to come back here 310 // again as long as the loop is still running. 311 bool did_work = delegate_->DoIdleWork(); 312 if (did_work) { 313 CFRunLoopSourceSignal(idle_work_source_); 314 } 315 316 return did_work; 317 } 318 319 // Called from the run loop. 320 // static 321 void MessagePumpCFRunLoopBase::RunNestingDeferredWorkSource(void* info) { 322 MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info); 323 self->RunNestingDeferredWork(); 324 } 325 326 // Called by MessagePumpCFRunLoopBase::RunNestingDeferredWorkSource. 327 bool MessagePumpCFRunLoopBase::RunNestingDeferredWork() { 328 if (!delegate_) { 329 // This point can be reached with a NULL delegate_ if Run is not on the 330 // stack but foreign code is spinning the CFRunLoop. There's no sense in 331 // attempting to do any work or signalling the work sources because 332 // without a delegate, work is not possible. 333 return false; 334 } 335 336 // Immediately try work in priority order. 337 if (!RunWork()) { 338 if (!RunIdleWork()) { 339 return false; 340 } 341 } else { 342 // Work was done. Arrange for the loop to try non-nestable idle work on 343 // a subsequent pass. 344 CFRunLoopSourceSignal(idle_work_source_); 345 } 346 347 return true; 348 } 349 350 // Called before the run loop goes to sleep or exits, or processes sources. 351 void MessagePumpCFRunLoopBase::MaybeScheduleNestingDeferredWork() { 352 // deepest_nesting_level_ is set as run loops are entered. If the deepest 353 // level encountered is deeper than the current level, a nested loop 354 // (relative to the current level) ran since the last time nesting-deferred 355 // work was scheduled. When that situation is encountered, schedule 356 // nesting-deferred work in case any work was deferred because nested work 357 // was disallowed. 358 if (deepest_nesting_level_ > nesting_level_) { 359 deepest_nesting_level_ = nesting_level_; 360 CFRunLoopSourceSignal(nesting_deferred_work_source_); 361 } 362 } 363 364 // Called from the run loop. 365 // static 366 void MessagePumpCFRunLoopBase::PreWaitObserver(CFRunLoopObserverRef observer, 367 CFRunLoopActivity activity, 368 void* info) { 369 MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info); 370 371 // Attempt to do some idle work before going to sleep. 372 self->RunIdleWork(); 373 374 // The run loop is about to go to sleep. If any of the work done since it 375 // started or woke up resulted in a nested run loop running, 376 // nesting-deferred work may have accumulated. Schedule it for processing 377 // if appropriate. 378 self->MaybeScheduleNestingDeferredWork(); 379 } 380 381 // Called from the run loop. 382 // static 383 void MessagePumpCFRunLoopBase::PreSourceObserver(CFRunLoopObserverRef observer, 384 CFRunLoopActivity activity, 385 void* info) { 386 MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info); 387 388 // The run loop has reached the top of the loop and is about to begin 389 // processing sources. If the last iteration of the loop at this nesting 390 // level did not sleep or exit, nesting-deferred work may have accumulated 391 // if a nested loop ran. Schedule nesting-deferred work for processing if 392 // appropriate. 393 self->MaybeScheduleNestingDeferredWork(); 394 } 395 396 // Called from the run loop. 397 // static 398 void MessagePumpCFRunLoopBase::EnterExitObserver(CFRunLoopObserverRef observer, 399 CFRunLoopActivity activity, 400 void* info) { 401 MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info); 402 403 switch (activity) { 404 case kCFRunLoopEntry: 405 ++self->nesting_level_; 406 if (self->nesting_level_ > self->deepest_nesting_level_) { 407 self->deepest_nesting_level_ = self->nesting_level_; 408 } 409 break; 410 411 case kCFRunLoopExit: 412 // Not all run loops go to sleep. If a run loop is stopped before it 413 // goes to sleep due to a CFRunLoopStop call, or if the timeout passed 414 // to CFRunLoopRunInMode expires, the run loop may proceed directly from 415 // handling sources to exiting without any sleep. This most commonly 416 // occurs when CFRunLoopRunInMode is passed a timeout of 0, causing it 417 // to make a single pass through the loop and exit without sleep. Some 418 // native loops use CFRunLoop in this way. Because PreWaitObserver will 419 // not be called in these case, MaybeScheduleNestingDeferredWork needs 420 // to be called here, as the run loop exits. 421 // 422 // MaybeScheduleNestingDeferredWork consults self->nesting_level_ 423 // to determine whether to schedule nesting-deferred work. It expects 424 // the nesting level to be set to the depth of the loop that is going 425 // to sleep or exiting. It must be called before decrementing the 426 // value so that the value still corresponds to the level of the exiting 427 // loop. 428 self->MaybeScheduleNestingDeferredWork(); 429 --self->nesting_level_; 430 break; 431 432 default: 433 break; 434 } 435 436 self->EnterExitRunLoop(activity); 437 } 438 439 // Called by MessagePumpCFRunLoopBase::EnterExitRunLoop. The default 440 // implementation is a no-op. 441 void MessagePumpCFRunLoopBase::EnterExitRunLoop(CFRunLoopActivity activity) { 442 } 443 444 // Base version returns a standard NSAutoreleasePool. 445 NSAutoreleasePool* MessagePumpCFRunLoopBase::CreateAutoreleasePool() { 446 return [[NSAutoreleasePool alloc] init]; 447 } 448 449 MessagePumpCFRunLoop::MessagePumpCFRunLoop() 450 : quit_pending_(false) { 451 } 452 453 MessagePumpCFRunLoop::~MessagePumpCFRunLoop() {} 454 455 // Called by MessagePumpCFRunLoopBase::DoRun. If other CFRunLoopRun loops were 456 // running lower on the run loop thread's stack when this object was created, 457 // the same number of CFRunLoopRun loops must be running for the outermost call 458 // to Run. Run/DoRun are reentrant after that point. 459 void MessagePumpCFRunLoop::DoRun(Delegate* delegate) { 460 // This is completely identical to calling CFRunLoopRun(), except autorelease 461 // pool management is introduced. 462 int result; 463 do { 464 MessagePumpScopedAutoreleasePool autorelease_pool(this); 465 result = CFRunLoopRunInMode(kCFRunLoopDefaultMode, 466 kCFTimeIntervalMax, 467 false); 468 } while (result != kCFRunLoopRunStopped && result != kCFRunLoopRunFinished); 469 } 470 471 // Must be called on the run loop thread. 472 void MessagePumpCFRunLoop::Quit() { 473 // Stop the innermost run loop managed by this MessagePumpCFRunLoop object. 474 if (nesting_level() == run_nesting_level()) { 475 // This object is running the innermost loop, just stop it. 476 CFRunLoopStop(run_loop()); 477 } else { 478 // There's another loop running inside the loop managed by this object. 479 // In other words, someone else called CFRunLoopRunInMode on the same 480 // thread, deeper on the stack than the deepest Run call. Don't preempt 481 // other run loops, just mark this object to quit the innermost Run as 482 // soon as the other inner loops not managed by Run are done. 483 quit_pending_ = true; 484 } 485 } 486 487 // Called by MessagePumpCFRunLoopBase::EnterExitObserver. 488 void MessagePumpCFRunLoop::EnterExitRunLoop(CFRunLoopActivity activity) { 489 if (activity == kCFRunLoopExit && 490 nesting_level() == run_nesting_level() && 491 quit_pending_) { 492 // Quit was called while loops other than those managed by this object 493 // were running further inside a run loop managed by this object. Now 494 // that all unmanaged inner run loops are gone, stop the loop running 495 // just inside Run. 496 CFRunLoopStop(run_loop()); 497 quit_pending_ = false; 498 } 499 } 500 501 MessagePumpNSRunLoop::MessagePumpNSRunLoop() 502 : keep_running_(true) { 503 CFRunLoopSourceContext source_context = CFRunLoopSourceContext(); 504 source_context.perform = NoOp; 505 quit_source_ = CFRunLoopSourceCreate(NULL, // allocator 506 0, // priority 507 &source_context); 508 CFRunLoopAddSource(run_loop(), quit_source_, kCFRunLoopCommonModes); 509 } 510 511 MessagePumpNSRunLoop::~MessagePumpNSRunLoop() { 512 CFRunLoopRemoveSource(run_loop(), quit_source_, kCFRunLoopCommonModes); 513 CFRelease(quit_source_); 514 } 515 516 void MessagePumpNSRunLoop::DoRun(Delegate* delegate) { 517 while (keep_running_) { 518 // NSRunLoop manages autorelease pools itself. 519 [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode 520 beforeDate:[NSDate distantFuture]]; 521 } 522 523 keep_running_ = true; 524 } 525 526 void MessagePumpNSRunLoop::Quit() { 527 keep_running_ = false; 528 CFRunLoopSourceSignal(quit_source_); 529 CFRunLoopWakeUp(run_loop()); 530 } 531 532 #if defined(OS_IOS) 533 MessagePumpUIApplication::MessagePumpUIApplication() 534 : run_loop_(NULL) { 535 } 536 537 MessagePumpUIApplication::~MessagePumpUIApplication() {} 538 539 void MessagePumpUIApplication::DoRun(Delegate* delegate) { 540 NOTREACHED(); 541 } 542 543 void MessagePumpUIApplication::Quit() { 544 NOTREACHED(); 545 } 546 547 void MessagePumpUIApplication::Attach(Delegate* delegate) { 548 DCHECK(!run_loop_); 549 run_loop_ = new RunLoop(); 550 CHECK(run_loop_->BeforeRun()); 551 SetDelegate(delegate); 552 } 553 554 #else 555 556 MessagePumpNSApplication::MessagePumpNSApplication() 557 : keep_running_(true), 558 running_own_loop_(false) { 559 } 560 561 MessagePumpNSApplication::~MessagePumpNSApplication() {} 562 563 void MessagePumpNSApplication::DoRun(Delegate* delegate) { 564 bool last_running_own_loop_ = running_own_loop_; 565 566 // NSApp must be initialized by calling: 567 // [{some class which implements CrAppProtocol} sharedApplication] 568 // Most likely candidates are CrApplication or BrowserCrApplication. 569 // These can be initialized from C++ code by calling 570 // RegisterCrApp() or RegisterBrowserCrApp(). 571 CHECK(NSApp); 572 573 if (![NSApp isRunning]) { 574 running_own_loop_ = false; 575 // NSApplication manages autorelease pools itself when run this way. 576 [NSApp run]; 577 } else { 578 running_own_loop_ = true; 579 NSDate* distant_future = [NSDate distantFuture]; 580 while (keep_running_) { 581 MessagePumpScopedAutoreleasePool autorelease_pool(this); 582 NSEvent* event = [NSApp nextEventMatchingMask:NSAnyEventMask 583 untilDate:distant_future 584 inMode:NSDefaultRunLoopMode 585 dequeue:YES]; 586 if (event) { 587 [NSApp sendEvent:event]; 588 } 589 } 590 keep_running_ = true; 591 } 592 593 running_own_loop_ = last_running_own_loop_; 594 } 595 596 void MessagePumpNSApplication::Quit() { 597 if (!running_own_loop_) { 598 [[NSApplication sharedApplication] stop:nil]; 599 } else { 600 keep_running_ = false; 601 } 602 603 // Send a fake event to wake the loop up. 604 [NSApp postEvent:[NSEvent otherEventWithType:NSApplicationDefined 605 location:NSZeroPoint 606 modifierFlags:0 607 timestamp:0 608 windowNumber:0 609 context:NULL 610 subtype:0 611 data1:0 612 data2:0] 613 atStart:NO]; 614 } 615 616 MessagePumpCrApplication::MessagePumpCrApplication() { 617 } 618 619 MessagePumpCrApplication::~MessagePumpCrApplication() { 620 } 621 622 // Prevents an autorelease pool from being created if the app is in the midst of 623 // handling a UI event because various parts of AppKit depend on objects that 624 // are created while handling a UI event to be autoreleased in the event loop. 625 // An example of this is NSWindowController. When a window with a window 626 // controller is closed it goes through a stack like this: 627 // (Several stack frames elided for clarity) 628 // 629 // #0 [NSWindowController autorelease] 630 // #1 DoAClose 631 // #2 MessagePumpCFRunLoopBase::DoWork() 632 // #3 [NSRunLoop run] 633 // #4 [NSButton performClick:] 634 // #5 [NSWindow sendEvent:] 635 // #6 [NSApp sendEvent:] 636 // #7 [NSApp run] 637 // 638 // -performClick: spins a nested run loop. If the pool created in DoWork was a 639 // standard NSAutoreleasePool, it would release the objects that were 640 // autoreleased into it once DoWork released it. This would cause the window 641 // controller, which autoreleased itself in frame #0, to release itself, and 642 // possibly free itself. Unfortunately this window controller controls the 643 // window in frame #5. When the stack is unwound to frame #5, the window would 644 // no longer exists and crashes may occur. Apple gets around this by never 645 // releasing the pool it creates in frame #4, and letting frame #7 clean it up 646 // when it cleans up the pool that wraps frame #7. When an autorelease pool is 647 // released it releases all other pools that were created after it on the 648 // autorelease pool stack. 649 // 650 // CrApplication is responsible for setting handlingSendEvent to true just 651 // before it sends the event through the event handling mechanism, and 652 // returning it to its previous value once the event has been sent. 653 NSAutoreleasePool* MessagePumpCrApplication::CreateAutoreleasePool() { 654 if (MessagePumpMac::IsHandlingSendEvent()) 655 return nil; 656 return MessagePumpNSApplication::CreateAutoreleasePool(); 657 } 658 659 // static 660 bool MessagePumpMac::UsingCrApp() { 661 DCHECK([NSThread isMainThread]); 662 663 // If NSApp is still not initialized, then the subclass used cannot 664 // be determined. 665 DCHECK(NSApp); 666 667 // The pump was created using MessagePumpNSApplication. 668 if (g_not_using_cr_app) 669 return false; 670 671 return [NSApp conformsToProtocol:@protocol(CrAppProtocol)]; 672 } 673 674 // static 675 bool MessagePumpMac::IsHandlingSendEvent() { 676 DCHECK([NSApp conformsToProtocol:@protocol(CrAppProtocol)]); 677 NSObject<CrAppProtocol>* app = static_cast<NSObject<CrAppProtocol>*>(NSApp); 678 return [app isHandlingSendEvent]; 679 } 680 #endif // !defined(OS_IOS) 681 682 // static 683 MessagePump* MessagePumpMac::Create() { 684 if ([NSThread isMainThread]) { 685 #if defined(OS_IOS) 686 return new MessagePumpUIApplication; 687 #else 688 if ([NSApp conformsToProtocol:@protocol(CrAppProtocol)]) 689 return new MessagePumpCrApplication; 690 691 // The main-thread MessagePump implementations REQUIRE an NSApp. 692 // Executables which have specific requirements for their 693 // NSApplication subclass should initialize appropriately before 694 // creating an event loop. 695 [NSApplication sharedApplication]; 696 g_not_using_cr_app = true; 697 return new MessagePumpNSApplication; 698 #endif 699 } 700 701 return new MessagePumpNSRunLoop; 702 } 703 704 } // namespace base 705