remove redundant DCHECK that a size_t variable >= 0
[chromium-blink-merge.git] / base / message_pump_mac.mm
blob4a07cd47df708188f656e31fc814f51443845aca
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.
5 #import "base/message_pump_mac.h"
7 #import <Foundation/Foundation.h>
9 #include <limits>
11 #include "base/logging.h"
12 #include "base/run_loop.h"
13 #include "base/time.h"
15 #if !defined(OS_IOS)
16 #import <AppKit/AppKit.h>
17 #endif  // !defined(OS_IOS)
19 namespace {
21 void NoOp(void* info) {
24 const CFTimeInterval kCFTimeIntervalMax =
25     std::numeric_limits<CFTimeInterval>::max();
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
33 }  // namespace
35 namespace base {
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   }
49  private:
50   NSAutoreleasePool* pool_;
51   DISALLOW_COPY_AND_ASSIGN(MessagePumpScopedAutoreleasePool);
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_);
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);
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);
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);
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);
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);
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);
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);
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_);
137   CFRunLoopRemoveObserver(run_loop_, pre_source_observer_,
138                           kCFRunLoopCommonModes);
139   CFRelease(pre_source_observer_);
141   CFRunLoopRemoveObserver(run_loop_, pre_wait_observer_,
142                           kCFRunLoopCommonModes);
143   CFRelease(pre_wait_observer_);
145   CFRunLoopRemoveSource(run_loop_, nesting_deferred_work_source_,
146                         kCFRunLoopCommonModes);
147   CFRelease(nesting_deferred_work_source_);
149   CFRunLoopRemoveSource(run_loop_, idle_work_source_, kCFRunLoopCommonModes);
150   CFRelease(idle_work_source_);
152   CFRunLoopRemoveSource(run_loop_, work_source_, kCFRunLoopCommonModes);
153   CFRelease(work_source_);
155   CFRunLoopRemoveTimer(run_loop_, delayed_work_timer_, kCFRunLoopCommonModes);
156   CFRelease(delayed_work_timer_);
158   CFRelease(run_loop_);
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;
168   Delegate* last_delegate = delegate_;
169   SetDelegate(delegate);
171   DoRun(delegate);
173   // Restore the previous state of the object.
174   SetDelegate(last_delegate);
175   run_nesting_level_ = last_run_nesting_level;
178 void MessagePumpCFRunLoopBase::SetDelegate(Delegate* delegate) {
179   delegate_ = delegate;
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   }
196 // May be called on any thread.
197 void MessagePumpCFRunLoopBase::ScheduleWork() {
198   CFRunLoopSourceSignal(work_source_);
199   CFRunLoopWakeUp(run_loop_);
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_);
210 // Called from the run loop.
211 // static
212 void MessagePumpCFRunLoopBase::RunDelayedWorkTimer(CFRunLoopTimerRef timer,
213                                                    void* info) {
214   MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info);
216   // The timer won't fire again until it's reset.
217   self->delayed_work_fire_time_ = kCFTimeIntervalMax;
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_);
226 // Called from the run loop.
227 // static
228 void MessagePumpCFRunLoopBase::RunWorkSource(void* info) {
229   MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info);
230   self->RunWork();
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   }
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);
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;
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   }
278   if (resignal_work_source) {
279     CFRunLoopSourceSignal(work_source_);
280   }
282   return resignal_work_source;
285 // Called from the run loop.
286 // static
287 void MessagePumpCFRunLoopBase::RunIdleWorkSource(void* info) {
288   MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info);
289   self->RunIdleWork();
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   }
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);
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   }
316   return did_work;
319 // Called from the run loop.
320 // static
321 void MessagePumpCFRunLoopBase::RunNestingDeferredWorkSource(void* info) {
322   MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info);
323   self->RunNestingDeferredWork();
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   }
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   }
347   return true;
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   }
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);
371   // Attempt to do some idle work before going to sleep.
372   self->RunIdleWork();
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();
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);
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();
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);
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;
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;
432     default:
433       break;
434   }
436   self->EnterExitRunLoop(activity);
439 // Called by MessagePumpCFRunLoopBase::EnterExitRunLoop.  The default
440 // implementation is a no-op.
441 void MessagePumpCFRunLoopBase::EnterExitRunLoop(CFRunLoopActivity activity) {
444 // Base version returns a standard NSAutoreleasePool.
445 NSAutoreleasePool* MessagePumpCFRunLoopBase::CreateAutoreleasePool() {
446   return [[NSAutoreleasePool alloc] init];
449 MessagePumpCFRunLoop::MessagePumpCFRunLoop()
450     : quit_pending_(false) {
453 MessagePumpCFRunLoop::~MessagePumpCFRunLoop() {}
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);
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   }
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   }
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);
511 MessagePumpNSRunLoop::~MessagePumpNSRunLoop() {
512   CFRunLoopRemoveSource(run_loop(), quit_source_, kCFRunLoopCommonModes);
513   CFRelease(quit_source_);
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   }
523   keep_running_ = true;
526 void MessagePumpNSRunLoop::Quit() {
527   keep_running_ = false;
528   CFRunLoopSourceSignal(quit_source_);
529   CFRunLoopWakeUp(run_loop());
532 #if defined(OS_IOS)
533 MessagePumpUIApplication::MessagePumpUIApplication()
534     : run_loop_(NULL) {
537 MessagePumpUIApplication::~MessagePumpUIApplication() {}
539 void MessagePumpUIApplication::DoRun(Delegate* delegate) {
540   NOTREACHED();
543 void MessagePumpUIApplication::Quit() {
544   NOTREACHED();
547 void MessagePumpUIApplication::Attach(Delegate* delegate) {
548   DCHECK(!run_loop_);
549   run_loop_ = new base::RunLoop();
550   CHECK(run_loop_->BeforeRun());
551   SetDelegate(delegate);
554 #else
556 MessagePumpNSApplication::MessagePumpNSApplication()
557     : keep_running_(true),
558       running_own_loop_(false) {
561 MessagePumpNSApplication::~MessagePumpNSApplication() {}
563 void MessagePumpNSApplication::DoRun(Delegate* delegate) {
564   bool last_running_own_loop_ = running_own_loop_;
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);
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   }
593   running_own_loop_ = last_running_own_loop_;
596 void MessagePumpNSApplication::Quit() {
597   if (!running_own_loop_) {
598     [[NSApplication sharedApplication] stop:nil];
599   } else {
600     keep_running_ = false;
601   }
603   // Send a fake event to wake the loop up.
604   [NSApp postEvent:[NSEvent otherEventWithType:NSApplicationDefined
605                                       location:NSMakePoint(0, 0)
606                                  modifierFlags:0
607                                      timestamp:0
608                                   windowNumber:0
609                                        context:NULL
610                                        subtype:0
611                                          data1:0
612                                          data2:0]
613            atStart:NO];
616 MessagePumpCrApplication::MessagePumpCrApplication() {
619 // Prevents an autorelease pool from being created if the app is in the midst of
620 // handling a UI event because various parts of AppKit depend on objects that
621 // are created while handling a UI event to be autoreleased in the event loop.
622 // An example of this is NSWindowController. When a window with a window
623 // controller is closed it goes through a stack like this:
624 // (Several stack frames elided for clarity)
626 // #0 [NSWindowController autorelease]
627 // #1 DoAClose
628 // #2 MessagePumpCFRunLoopBase::DoWork()
629 // #3 [NSRunLoop run]
630 // #4 [NSButton performClick:]
631 // #5 [NSWindow sendEvent:]
632 // #6 [NSApp sendEvent:]
633 // #7 [NSApp run]
635 // -performClick: spins a nested run loop. If the pool created in DoWork was a
636 // standard NSAutoreleasePool, it would release the objects that were
637 // autoreleased into it once DoWork released it. This would cause the window
638 // controller, which autoreleased itself in frame #0, to release itself, and
639 // possibly free itself. Unfortunately this window controller controls the
640 // window in frame #5. When the stack is unwound to frame #5, the window would
641 // no longer exists and crashes may occur. Apple gets around this by never
642 // releasing the pool it creates in frame #4, and letting frame #7 clean it up
643 // when it cleans up the pool that wraps frame #7. When an autorelease pool is
644 // released it releases all other pools that were created after it on the
645 // autorelease pool stack.
647 // CrApplication is responsible for setting handlingSendEvent to true just
648 // before it sends the event through the event handling mechanism, and
649 // returning it to its previous value once the event has been sent.
650 NSAutoreleasePool* MessagePumpCrApplication::CreateAutoreleasePool() {
651   if (MessagePumpMac::IsHandlingSendEvent())
652     return nil;
653   return MessagePumpNSApplication::CreateAutoreleasePool();
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 (g_not_using_cr_app)
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];
677 #endif  // !defined(OS_IOS)
679 // static
680 MessagePump* MessagePumpMac::Create() {
681   if ([NSThread isMainThread]) {
682 #if defined(OS_IOS)
683     return new MessagePumpUIApplication;
684 #else
685     if ([NSApp conformsToProtocol:@protocol(CrAppProtocol)])
686       return new MessagePumpCrApplication;
688     // The main-thread MessagePump implementations REQUIRE an NSApp.
689     // Executables which have specific requirements for their
690     // NSApplication subclass should initialize appropriately before
691     // creating an event loop.
692     [NSApplication sharedApplication];
693     g_not_using_cr_app = true;
694     return new MessagePumpNSApplication;
695 #endif
696   }
698   return new MessagePumpNSRunLoop;
701 }  // namespace base