Another take on menu's. This uses the hosting menu scroll view container as a menuba...
[chromium-blink-merge.git] / base / message_pump_mac.mm
blob16a9a599708199c0a36da42a96204ef781cf0b1e
1 // Copyright (c) 2008 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 #include "base/message_pump_mac.h"
7 #import <AppKit/AppKit.h>
8 #import <Foundation/Foundation.h>
9 #include <IOKit/IOMessage.h>
10 #include <IOKit/pwr_mgt/IOPMLib.h>
12 #include <limits>
14 #import "base/chrome_application_mac.h"
15 #include "base/logging.h"
16 #include "base/time.h"
18 namespace {
20 void NoOp(void* info) {
23 const CFTimeInterval kCFTimeIntervalMax =
24     std::numeric_limits<CFTimeInterval>::max();
26 }  // namespace
28 namespace base {
30 // A scoper for autorelease pools created from message pump run loops.
31 // Avoids dirtying up the ScopedNSAutoreleasePool interface for the rare
32 // case where an autorelease pool needs to be passed in.
33 class MessagePumpScopedAutoreleasePool {
34  public:
35   explicit MessagePumpScopedAutoreleasePool(MessagePumpCFRunLoopBase* pump) :
36       pool_(pump->CreateAutoreleasePool()) {
37   }
38    ~MessagePumpScopedAutoreleasePool() {
39     [pool_ drain];
40   }
42  private:
43   NSAutoreleasePool* pool_;
44   DISALLOW_COPY_AND_ASSIGN(MessagePumpScopedAutoreleasePool);
47 // Must be called on the run loop thread.
48 MessagePumpCFRunLoopBase::MessagePumpCFRunLoopBase()
49     : delegate_(NULL),
50       delayed_work_fire_time_(kCFTimeIntervalMax),
51       nesting_level_(0),
52       run_nesting_level_(0),
53       deepest_nesting_level_(0),
54       delegateless_work_(false),
55       delegateless_delayed_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 = RunDelayedWorkSource;
83   delayed_work_source_ = CFRunLoopSourceCreate(NULL,  // allocator
84                                                2,     // priority
85                                                &source_context);
86   CFRunLoopAddSource(run_loop_, delayed_work_source_, kCFRunLoopCommonModes);
88   source_context.perform = RunIdleWorkSource;
89   idle_work_source_ = CFRunLoopSourceCreate(NULL,  // allocator
90                                             3,     // 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);
128   root_power_domain_ = IORegisterForSystemPower(this,
129                                                 &power_notification_port_,
130                                                 PowerStateNotification,
131                                                 &power_notification_object_);
132   if (root_power_domain_ != MACH_PORT_NULL) {
133     CFRunLoopAddSource(
134         run_loop_,
135         IONotificationPortGetRunLoopSource(power_notification_port_),
136         kCFRunLoopCommonModes);
137   }
140 // Ideally called on the run loop thread.  If other run loops were running
141 // lower on the run loop thread's stack when this object was created, the
142 // same number of run loops must be running when this object is destroyed.
143 MessagePumpCFRunLoopBase::~MessagePumpCFRunLoopBase() {
144   if (root_power_domain_ != MACH_PORT_NULL) {
145     CFRunLoopRemoveSource(
146         run_loop_,
147         IONotificationPortGetRunLoopSource(power_notification_port_),
148         kCFRunLoopCommonModes);
149     IODeregisterForSystemPower(&power_notification_object_);
150     IOServiceClose(root_power_domain_);
151     IONotificationPortDestroy(power_notification_port_);
152   }
154   CFRunLoopRemoveObserver(run_loop_, enter_exit_observer_,
155                           kCFRunLoopCommonModes);
156   CFRelease(enter_exit_observer_);
158   CFRunLoopRemoveObserver(run_loop_, pre_source_observer_,
159                           kCFRunLoopCommonModes);
160   CFRelease(pre_source_observer_);
162   CFRunLoopRemoveObserver(run_loop_, pre_wait_observer_,
163                           kCFRunLoopCommonModes);
164   CFRelease(pre_wait_observer_);
166   CFRunLoopRemoveSource(run_loop_, nesting_deferred_work_source_,
167                         kCFRunLoopCommonModes);
168   CFRelease(nesting_deferred_work_source_);
170   CFRunLoopRemoveSource(run_loop_, idle_work_source_, kCFRunLoopCommonModes);
171   CFRelease(idle_work_source_);
173   CFRunLoopRemoveSource(run_loop_, delayed_work_source_, kCFRunLoopCommonModes);
174   CFRelease(delayed_work_source_);
176   CFRunLoopRemoveSource(run_loop_, work_source_, kCFRunLoopCommonModes);
177   CFRelease(work_source_);
179   CFRunLoopRemoveTimer(run_loop_, delayed_work_timer_, kCFRunLoopCommonModes);
180   CFRelease(delayed_work_timer_);
182   CFRelease(run_loop_);
185 // Must be called on the run loop thread.
186 void MessagePumpCFRunLoopBase::Run(Delegate* delegate) {
187   // nesting_level_ will be incremented in EnterExitRunLoop, so set
188   // run_nesting_level_ accordingly.
189   int last_run_nesting_level = run_nesting_level_;
190   run_nesting_level_ = nesting_level_ + 1;
192   Delegate* last_delegate = delegate_;
193   delegate_ = delegate;
195   if (delegate) {
196     // If any work showed up but could not be dispatched for want of a
197     // delegate, set it up for dispatch again now that a delegate is
198     // available.
199     if (delegateless_work_) {
200       CFRunLoopSourceSignal(work_source_);
201       delegateless_work_ = false;
202     }
203     if (delegateless_delayed_work_) {
204       CFRunLoopSourceSignal(delayed_work_source_);
205       delegateless_delayed_work_ = false;
206     }
207     if (delegateless_idle_work_) {
208       CFRunLoopSourceSignal(idle_work_source_);
209       delegateless_idle_work_ = false;
210     }
211   }
213   DoRun(delegate);
215   // Restore the previous state of the object.
216   delegate_ = last_delegate;
217   run_nesting_level_ = last_run_nesting_level;
220 // May be called on any thread.
221 void MessagePumpCFRunLoopBase::ScheduleWork() {
222   CFRunLoopSourceSignal(work_source_);
223   CFRunLoopWakeUp(run_loop_);
226 // Must be called on the run loop thread.
227 void MessagePumpCFRunLoopBase::ScheduleDelayedWork(
228     const Time& delayed_work_time) {
229   Time::Exploded exploded;
230   delayed_work_time.UTCExplode(&exploded);
231   double seconds = exploded.second +
232                    (static_cast<double>((delayed_work_time.ToInternalValue()) %
233                                         Time::kMicrosecondsPerSecond) /
234                     Time::kMicrosecondsPerSecond);
235   CFGregorianDate gregorian = {
236     exploded.year,
237     exploded.month,
238     exploded.day_of_month,
239     exploded.hour,
240     exploded.minute,
241     seconds
242   };
243   delayed_work_fire_time_ = CFGregorianDateGetAbsoluteTime(gregorian, NULL);
245   CFRunLoopTimerSetNextFireDate(delayed_work_timer_, delayed_work_fire_time_);
248 // Called from the run loop.
249 // static
250 void MessagePumpCFRunLoopBase::RunDelayedWorkTimer(CFRunLoopTimerRef timer,
251                                                    void* info) {
252   MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info);
254   // The timer won't fire again until it's reset.
255   self->delayed_work_fire_time_ = kCFTimeIntervalMax;
257   // CFRunLoopTimers fire outside of the priority scheme for CFRunLoopSources.
258   // In order to establish the proper priority where delegate_->DoDelayedWork
259   // can only be called if delegate_->DoWork returns false, the timer used
260   // to schedule delayed work must signal a CFRunLoopSource set at a lower
261   // priority than the one used for delegate_->DoWork.
262   CFRunLoopSourceSignal(self->delayed_work_source_);
265 // Called from the run loop.
266 // static
267 void MessagePumpCFRunLoopBase::RunWorkSource(void* info) {
268   MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info);
269   self->RunWork();
272 // Called by MessagePumpCFRunLoopBase::RunWorkSource.
273 bool MessagePumpCFRunLoopBase::RunWork() {
274   if (!delegate_) {
275     // This point can be reached with a NULL delegate_ if Run is not on the
276     // stack but foreign code is spinning the CFRunLoop.  Arrange to come back
277     // here when a delegate is available.
278     delegateless_work_ = true;
279     return false;
280   }
282   // The NSApplication-based run loop only drains the autorelease pool at each
283   // UI event (NSEvent).  The autorelease pool is not drained for each
284   // CFRunLoopSource target that's run.  Use a local pool for any autoreleased
285   // objects if the app is not currently handling a UI event to ensure they're
286   // released promptly even in the absence of UI events.
287   MessagePumpScopedAutoreleasePool autorelease_pool(this);
289   // Call DoWork once, and if something was done, arrange to come back here
290   // again as long as the loop is still running.
291   bool did_work = delegate_->DoWork();
292   if (did_work) {
293     CFRunLoopSourceSignal(work_source_);
294   }
296   return did_work;
299 // Called from the run loop.
300 // static
301 void MessagePumpCFRunLoopBase::RunDelayedWorkSource(void* info) {
302   MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info);
303   self->RunDelayedWork();
306 // Called by MessagePumpCFRunLoopBase::RunDelayedWorkSource.
307 bool MessagePumpCFRunLoopBase::RunDelayedWork() {
308   if (!delegate_) {
309     // This point can be reached with a NULL delegate_ if Run is not on the
310     // stack but foreign code is spinning the CFRunLoop.  Arrange to come back
311     // here when a delegate is available.
312     delegateless_delayed_work_ = true;
313     return false;
314   }
316   // The NSApplication-based run loop only drains the autorelease pool at each
317   // UI event (NSEvent).  The autorelease pool is not drained for each
318   // CFRunLoopSource target that's run.  Use a local pool for any autoreleased
319   // objects if the app is not currently handling a UI event to ensure they're
320   // released promptly even in the absence of UI events.
321   MessagePumpScopedAutoreleasePool autorelease_pool(this);
323   Time next_time;
324   delegate_->DoDelayedWork(&next_time);
326   bool more_work = !next_time.is_null();
327   if (more_work) {
328     TimeDelta delay = next_time - Time::Now();
329     if (delay > TimeDelta()) {
330       // There's more delayed work to be done in the future.
331       ScheduleDelayedWork(next_time);
332     } else {
333       // There's more delayed work to be done, and its time is in the past.
334       // Arrange to come back here directly as long as the loop is still
335       // running.
336       CFRunLoopSourceSignal(delayed_work_source_);
337     }
338   }
340   return more_work;
343 // Called from the run loop.
344 // static
345 void MessagePumpCFRunLoopBase::RunIdleWorkSource(void* info) {
346   MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info);
347   self->RunIdleWork();
350 // Called by MessagePumpCFRunLoopBase::RunIdleWorkSource.
351 bool MessagePumpCFRunLoopBase::RunIdleWork() {
352   if (!delegate_) {
353     // This point can be reached with a NULL delegate_ if Run is not on the
354     // stack but foreign code is spinning the CFRunLoop.  Arrange to come back
355     // here when a delegate is available.
356     delegateless_idle_work_ = true;
357     return false;
358   }
360   // The NSApplication-based run loop only drains the autorelease pool at each
361   // UI event (NSEvent).  The autorelease pool is not drained for each
362   // CFRunLoopSource target that's run.  Use a local pool for any autoreleased
363   // objects if the app is not currently handling a UI event to ensure they're
364   // released promptly even in the absence of UI events.
365   MessagePumpScopedAutoreleasePool autorelease_pool(this);
367   // Call DoIdleWork once, and if something was done, arrange to come back here
368   // again as long as the loop is still running.
369   bool did_work = delegate_->DoIdleWork();
370   if (did_work) {
371     CFRunLoopSourceSignal(idle_work_source_);
372   }
374   return did_work;
377 // Called from the run loop.
378 // static
379 void MessagePumpCFRunLoopBase::RunNestingDeferredWorkSource(void* info) {
380   MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info);
381   self->RunNestingDeferredWork();
384 // Called by MessagePumpCFRunLoopBase::RunNestingDeferredWorkSource.
385 bool MessagePumpCFRunLoopBase::RunNestingDeferredWork() {
386   if (!delegate_) {
387     // This point can be reached with a NULL delegate_ if Run is not on the
388     // stack but foreign code is spinning the CFRunLoop.  There's no sense in
389     // attempting to do any work or signalling the work sources because
390     // without a delegate, work is not possible.
391     return false;
392   }
394   // Immediately try work in priority order.
395   if (!RunWork()) {
396     if (!RunDelayedWork()) {
397       if (!RunIdleWork()) {
398         return false;
399       }
400     } else {
401       // There was no work, and delayed work was done.  Arrange for the loop
402       // to try non-nestable idle work on a subsequent pass.
403       CFRunLoopSourceSignal(idle_work_source_);
404     }
405   } else {
406     // Work was done.  Arrange for the loop to try non-nestable delayed and
407     // idle work on a subsequent pass.
408     CFRunLoopSourceSignal(delayed_work_source_);
409     CFRunLoopSourceSignal(idle_work_source_);
410   }
412   return true;
415 // Called before the run loop goes to sleep or exits, or processes sources.
416 void MessagePumpCFRunLoopBase::MaybeScheduleNestingDeferredWork() {
417   // deepest_nesting_level_ is set as run loops are entered.  If the deepest
418   // level encountered is deeper than the current level, a nested loop
419   // (relative to the current level) ran since the last time nesting-deferred
420   // work was scheduled.  When that situation is encountered, schedule
421   // nesting-deferred work in case any work was deferred because nested work
422   // was disallowed.
423   if (deepest_nesting_level_ > nesting_level_) {
424     deepest_nesting_level_ = nesting_level_;
425     CFRunLoopSourceSignal(nesting_deferred_work_source_);
426   }
429 // Called from the run loop.
430 // static
431 void MessagePumpCFRunLoopBase::PreWaitObserver(CFRunLoopObserverRef observer,
432                                                CFRunLoopActivity activity,
433                                                void* info) {
434   MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info);
436   // Attempt to do some idle work before going to sleep.
437   self->RunIdleWork();
439   // The run loop is about to go to sleep.  If any of the work done since it
440   // started or woke up resulted in a nested run loop running,
441   // nesting-deferred work may have accumulated.  Schedule it for processing
442   // if appropriate.
443   self->MaybeScheduleNestingDeferredWork();
446 // Called from the run loop.
447 // static
448 void MessagePumpCFRunLoopBase::PreSourceObserver(CFRunLoopObserverRef observer,
449                                                  CFRunLoopActivity activity,
450                                                  void* info) {
451   MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info);
453   // The run loop has reached the top of the loop and is about to begin
454   // processing sources.  If the last iteration of the loop at this nesting
455   // level did not sleep or exit, nesting-deferred work may have accumulated
456   // if a nested loop ran.  Schedule nesting-deferred work for processing if
457   // appropriate.
458   self->MaybeScheduleNestingDeferredWork();
461 // Called from the run loop.
462 // static
463 void MessagePumpCFRunLoopBase::EnterExitObserver(CFRunLoopObserverRef observer,
464                                                  CFRunLoopActivity activity,
465                                                  void* info) {
466   MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info);
468   switch (activity) {
469     case kCFRunLoopEntry:
470       ++self->nesting_level_;
471       if (self->nesting_level_ > self->deepest_nesting_level_) {
472         self->deepest_nesting_level_ = self->nesting_level_;
473       }
474       break;
476     case kCFRunLoopExit:
477       // Not all run loops go to sleep.  If a run loop is stopped before it
478       // goes to sleep due to a CFRunLoopStop call, or if the timeout passed
479       // to CFRunLoopRunInMode expires, the run loop may proceed directly from
480       // handling sources to exiting without any sleep.  This most commonly
481       // occurs when CFRunLoopRunInMode is passed a timeout of 0, causing it
482       // to make a single pass through the loop and exit without sleep.  Some
483       // native loops use CFRunLoop in this way.  Because PreWaitObserver will
484       // not be called in these case, MaybeScheduleNestingDeferredWork needs
485       // to be called here, as the run loop exits.
486       //
487       // MaybeScheduleNestingDeferredWork consults self->nesting_level_
488       // to determine whether to schedule nesting-deferred work.  It expects
489       // the nesting level to be set to the depth of the loop that is going
490       // to sleep or exiting.  It must be called before decrementing the
491       // value so that the value still corresponds to the level of the exiting
492       // loop.
493       self->MaybeScheduleNestingDeferredWork();
494       --self->nesting_level_;
495       break;
497     default:
498       break;
499   }
501   self->EnterExitRunLoop(activity);
504 // Called from the run loop.
505 // static
506 void MessagePumpCFRunLoopBase::PowerStateNotification(void* info,
507                                                       io_service_t service,
508                                                       uint32_t message_type,
509                                                       void* message_argument) {
510   // CFRunLoopTimer (NSTimer) is scheduled in terms of CFAbsoluteTime, which
511   // measures the number of seconds since 2001-01-01 00:00:00.0 Z.  It is
512   // implemented in terms of kernel ticks, as in mach_absolute_time.  While an
513   // offset and scale factor can be applied to convert between the two time
514   // bases at any time after boot, the kernel clock stops while the system is
515   // asleep, altering the offset.  (The offset will also change when the
516   // real-time clock is adjusted.)  CFRunLoopTimers are not readjusted to take
517   // this into account when the system wakes up, so any timers that were
518   // pending while the system was asleep will be delayed by the sleep
519   // duration.
520   //
521   // The MessagePump interface assumes that scheduled delayed work will be
522   // performed at the time ScheduleDelayedWork was asked to perform it.  The
523   // delay caused by the CFRunLoopTimer not firing at the appropriate time
524   // results in a stall of queued delayed work when the system wakes up.
525   // With this limitation, scheduled work would not be performed until
526   // (system wake time + scheduled work time - system sleep time), while it
527   // would be expected to be performed at (scheduled work time).
528   //
529   // To work around this problem, when the system wakes up from sleep, if a
530   // delayed work timer is pending, it is rescheduled to fire at the original
531   // time that it was scheduled to fire.
532   //
533   // This mechanism is not resilient if the real-time clock does not maintain
534   // stable time while the system is sleeping, but it matches the behavior of
535   // the various other MessagePump implementations, and MessageLoop seems to
536   // be limited in the same way.
537   //
538   // References
539   //  - Chris Kane, "NSTimer and deep sleep," cocoa-dev@lists.apple.com,
540   //    http://lists.apple.com/archives/Cocoa-dev/2002/May/msg01547.html
541   //  - Apple Technical Q&A QA1340, "Registering and unregistering for sleep
542   //    and wake notifications,"
543   //    http://developer.apple.com/mac/library/qa/qa2004/qa1340.html
544   //  - Core Foundation source code, CF-550/CFRunLoop.c and CF-550/CFDate.c,
545   //    http://www.opensource.apple.com/
547   MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info);
549   switch (message_type) {
550     case kIOMessageSystemWillPowerOn:
551       if (self->delayed_work_fire_time_ != kCFTimeIntervalMax) {
552         CFRunLoopTimerSetNextFireDate(self->delayed_work_timer_,
553                                       self->delayed_work_fire_time_);
554       }
555       break;
557     case kIOMessageSystemWillSleep:
558     case kIOMessageCanSystemSleep:
559       // The system will wait for 30 seconds before entering sleep if neither
560       // IOAllowPowerChange nor IOCancelPowerChange are called.  That would be
561       // pretty antisocial.
562       IOAllowPowerChange(self->root_power_domain_,
563                          reinterpret_cast<long>(message_argument));
564       break;
566     default:
567       break;
568   }
571 // Called by MessagePumpCFRunLoopBase::EnterExitRunLoop.  The default
572 // implementation is a no-op.
573 void MessagePumpCFRunLoopBase::EnterExitRunLoop(CFRunLoopActivity activity) {
576 // Base version returns a standard NSAutoreleasePool.
577 NSAutoreleasePool* MessagePumpCFRunLoopBase::CreateAutoreleasePool() {
578   return [[NSAutoreleasePool alloc] init];
581 MessagePumpCFRunLoop::MessagePumpCFRunLoop()
582     : quit_pending_(false) {
585 // Called by MessagePumpCFRunLoopBase::DoRun.  If other CFRunLoopRun loops were
586 // running lower on the run loop thread's stack when this object was created,
587 // the same number of CFRunLoopRun loops must be running for the outermost call
588 // to Run.  Run/DoRun are reentrant after that point.
589 void MessagePumpCFRunLoop::DoRun(Delegate* delegate) {
590   // This is completely identical to calling CFRunLoopRun(), except autorelease
591   // pool management is introduced.
592   int result;
593   do {
594     MessagePumpScopedAutoreleasePool autorelease_pool(this);
595     result = CFRunLoopRunInMode(kCFRunLoopDefaultMode,
596                                 kCFTimeIntervalMax,
597                                 false);
598   } while (result != kCFRunLoopRunStopped && result != kCFRunLoopRunFinished);
601 // Must be called on the run loop thread.
602 void MessagePumpCFRunLoop::Quit() {
603   // Stop the innermost run loop managed by this MessagePumpCFRunLoop object.
604   if (nesting_level() == run_nesting_level()) {
605     // This object is running the innermost loop, just stop it.
606     CFRunLoopStop(run_loop());
607   } else {
608     // There's another loop running inside the loop managed by this object.
609     // In other words, someone else called CFRunLoopRunInMode on the same
610     // thread, deeper on the stack than the deepest Run call.  Don't preempt
611     // other run loops, just mark this object to quit the innermost Run as
612     // soon as the other inner loops not managed by Run are done.
613     quit_pending_ = true;
614   }
617 // Called by MessagePumpCFRunLoopBase::EnterExitObserver.
618 void MessagePumpCFRunLoop::EnterExitRunLoop(CFRunLoopActivity activity) {
619   if (activity == kCFRunLoopExit &&
620       nesting_level() == run_nesting_level() &&
621       quit_pending_) {
622     // Quit was called while loops other than those managed by this object
623     // were running further inside a run loop managed by this object.  Now
624     // that all unmanaged inner run loops are gone, stop the loop running
625     // just inside Run.
626     CFRunLoopStop(run_loop());
627     quit_pending_ = false;
628   }
631 MessagePumpNSRunLoop::MessagePumpNSRunLoop()
632     : keep_running_(true) {
633   CFRunLoopSourceContext source_context = CFRunLoopSourceContext();
634   source_context.perform = NoOp;
635   quit_source_ = CFRunLoopSourceCreate(NULL,  // allocator
636                                        0,     // priority
637                                        &source_context);
638   CFRunLoopAddSource(run_loop(), quit_source_, kCFRunLoopCommonModes);
641 MessagePumpNSRunLoop::~MessagePumpNSRunLoop() {
642   CFRunLoopRemoveSource(run_loop(), quit_source_, kCFRunLoopCommonModes);
643   CFRelease(quit_source_);
646 void MessagePumpNSRunLoop::DoRun(Delegate* delegate) {
647   while (keep_running_) {
648     // NSRunLoop manages autorelease pools itself.
649     [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode
650                              beforeDate:[NSDate distantFuture]];
651   }
653   keep_running_ = true;
656 void MessagePumpNSRunLoop::Quit() {
657   keep_running_ = false;
658   CFRunLoopSourceSignal(quit_source_);
659   CFRunLoopWakeUp(run_loop());
662 MessagePumpNSApplication::MessagePumpNSApplication()
663     : keep_running_(true),
664       running_own_loop_(false) {
667 void MessagePumpNSApplication::DoRun(Delegate* delegate) {
668   bool last_running_own_loop_ = running_own_loop_;
670   // TODO(dmaclach): Get rid of this gratuitous sharedApplication.
671   // Tests should be setting up their applications on their own.
672   [CrApplication sharedApplication];
674   if (![NSApp isRunning]) {
675     running_own_loop_ = false;
676     // NSApplication manages autorelease pools itself when run this way.
677     [NSApp run];
678   } else {
679     running_own_loop_ = true;
680     NSDate* distant_future = [NSDate distantFuture];
681     while (keep_running_) {
682       MessagePumpScopedAutoreleasePool autorelease_pool(this);
683       NSEvent* event = [NSApp nextEventMatchingMask:NSAnyEventMask
684                                           untilDate:distant_future
685                                              inMode:NSDefaultRunLoopMode
686                                             dequeue:YES];
687       if (event) {
688         [NSApp sendEvent:event];
689       }
690     }
691     keep_running_ = true;
692   }
694   running_own_loop_ = last_running_own_loop_;
697 void MessagePumpNSApplication::Quit() {
698   if (!running_own_loop_) {
699     [NSApp stop:nil];
700   } else {
701     keep_running_ = false;
702   }
704   // Send a fake event to wake the loop up.
705   [NSApp postEvent:[NSEvent otherEventWithType:NSApplicationDefined
706                                       location:NSMakePoint(0, 0)
707                                  modifierFlags:0
708                                      timestamp:0
709                                   windowNumber:0
710                                        context:NULL
711                                        subtype:0
712                                          data1:0
713                                          data2:0]
714            atStart:NO];
717 // Prevents an autorelease pool from being created if the app is in the midst of
718 // handling a UI event because various parts of AppKit depend on objects that
719 // are created while handling a UI event to be autoreleased in the event loop.
720 // An example of this is NSWindowController. When a window with a window
721 // controller is closed it goes through a stack like this:
722 // (Several stack frames elided for clarity)
724 // #0 [NSWindowController autorelease]
725 // #1 DoAClose
726 // #2 MessagePumpCFRunLoopBase::DoWork()
727 // #3 [NSRunLoop run]
728 // #4 [NSButton performClick:]
729 // #5 [NSWindow sendEvent:]
730 // #6 [NSApp sendEvent:]
731 // #7 [NSApp run]
733 // -performClick: spins a nested run loop. If the pool created in DoWork was a
734 // standard NSAutoreleasePool, it would release the objects that were
735 // autoreleased into it once DoWork released it. This would cause the window
736 // controller, which autoreleased itself in frame #0, to release itself, and
737 // possibly free itself. Unfortunately this window controller controls the
738 // window in frame #5. When the stack is unwound to frame #5, the window would
739 // no longer exists and crashes may occur. Apple gets around this by never
740 // releasing the pool it creates in frame #4, and letting frame #7 clean it up
741 // when it cleans up the pool that wraps frame #7. When an autorelease pool is
742 // released it releases all other pools that were created after it on the
743 // autorelease pool stack.
745 // CrApplication is responsible for setting handlingSendEvent to true just
746 // before it sends the event throught the event handling mechanism, and
747 // returning it to its previous value once the event has been sent.
748 NSAutoreleasePool* MessagePumpNSApplication::CreateAutoreleasePool() {
749   NSAutoreleasePool* pool = nil;
750   DCHECK([NSApp isKindOfClass:[CrApplication class]]);
751   if (![static_cast<CrApplication*>(NSApp) isHandlingSendEvent]) {
752     pool = MessagePumpCFRunLoopBase::CreateAutoreleasePool();
753   }
754   return pool;
757 // static
758 MessagePump* MessagePumpMac::Create() {
759   if ([NSThread isMainThread]) {
760     return new MessagePumpNSApplication;
761   }
763   return new MessagePumpNSRunLoop;
766 }  // namespace base