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