Home | History | Annotate | Download | only in message_loop
      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