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