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