Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
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) {
21 }
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);
45 };
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 }
138 }
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_);
183 }
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;
218 }
220 // May be called on any thread.
221 void MessagePumpCFRunLoopBase::ScheduleWork() {
222 CFRunLoopSourceSignal(work_source_);
223 CFRunLoopWakeUp(run_loop_);
224 }
226 // Must be called on the run loop thread.
227 void MessagePumpCFRunLoopBase::ScheduleDelayedWork(
228 const TimeTicks& delayed_work_time) {
229 TimeDelta delta = delayed_work_time - TimeTicks::Now();
230 delayed_work_fire_time_ = CFAbsoluteTimeGetCurrent() + delta.InSecondsF();
231 CFRunLoopTimerSetNextFireDate(delayed_work_timer_, delayed_work_fire_time_);
232 }
234 // Called from the run loop.
235 // static
236 void MessagePumpCFRunLoopBase::RunDelayedWorkTimer(CFRunLoopTimerRef timer,
237 void* info) {
238 MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info);
240 // The timer won't fire again until it's reset.
241 self->delayed_work_fire_time_ = kCFTimeIntervalMax;
243 // CFRunLoopTimers fire outside of the priority scheme for CFRunLoopSources.
244 // In order to establish the proper priority where delegate_->DoDelayedWork
245 // can only be called if delegate_->DoWork returns false, the timer used
246 // to schedule delayed work must signal a CFRunLoopSource set at a lower
247 // priority than the one used for delegate_->DoWork.
248 CFRunLoopSourceSignal(self->delayed_work_source_);
249 }
251 // Called from the run loop.
252 // static
253 void MessagePumpCFRunLoopBase::RunWorkSource(void* info) {
254 MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info);
255 self->RunWork();
256 }
258 // Called by MessagePumpCFRunLoopBase::RunWorkSource.
259 bool MessagePumpCFRunLoopBase::RunWork() {
260 if (!delegate_) {
261 // This point can be reached with a NULL delegate_ if Run is not on the
262 // stack but foreign code is spinning the CFRunLoop. Arrange to come back
263 // here when a delegate is available.
264 delegateless_work_ = true;
265 return false;
266 }
268 // The NSApplication-based run loop only drains the autorelease pool at each
269 // UI event (NSEvent). The autorelease pool is not drained for each
270 // CFRunLoopSource target that's run. Use a local pool for any autoreleased
271 // objects if the app is not currently handling a UI event to ensure they're
272 // released promptly even in the absence of UI events.
273 MessagePumpScopedAutoreleasePool autorelease_pool(this);
275 // Call DoWork once, and if something was done, arrange to come back here
276 // again as long as the loop is still running.
277 bool did_work = delegate_->DoWork();
278 if (did_work) {
279 CFRunLoopSourceSignal(work_source_);
280 }
282 return did_work;
283 }
285 // Called from the run loop.
286 // static
287 void MessagePumpCFRunLoopBase::RunDelayedWorkSource(void* info) {
288 MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info);
289 self->RunDelayedWork();
290 }
292 // Called by MessagePumpCFRunLoopBase::RunDelayedWorkSource.
293 bool MessagePumpCFRunLoopBase::RunDelayedWork() {
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_delayed_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 TimeTicks next_time;
310 delegate_->DoDelayedWork(&next_time);
312 bool more_work = !next_time.is_null();
313 if (more_work) {
314 TimeDelta delay = next_time - TimeTicks::Now();
315 if (delay > TimeDelta()) {
316 // There's more delayed work to be done in the future.
317 ScheduleDelayedWork(next_time);
318 } else {
319 // There's more delayed work to be done, and its time is in the past.
320 // Arrange to come back here directly as long as the loop is still
321 // running.
322 CFRunLoopSourceSignal(delayed_work_source_);
323 }
324 }
326 return more_work;
327 }
329 // Called from the run loop.
330 // static
331 void MessagePumpCFRunLoopBase::RunIdleWorkSource(void* info) {
332 MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info);
333 self->RunIdleWork();
334 }
336 // Called by MessagePumpCFRunLoopBase::RunIdleWorkSource.
337 bool MessagePumpCFRunLoopBase::RunIdleWork() {
338 if (!delegate_) {
339 // This point can be reached with a NULL delegate_ if Run is not on the
340 // stack but foreign code is spinning the CFRunLoop. Arrange to come back
341 // here when a delegate is available.
342 delegateless_idle_work_ = true;
343 return false;
344 }
346 // The NSApplication-based run loop only drains the autorelease pool at each
347 // UI event (NSEvent). The autorelease pool is not drained for each
348 // CFRunLoopSource target that's run. Use a local pool for any autoreleased
349 // objects if the app is not currently handling a UI event to ensure they're
350 // released promptly even in the absence of UI events.
351 MessagePumpScopedAutoreleasePool autorelease_pool(this);
353 // Call DoIdleWork once, and if something was done, arrange to come back here
354 // again as long as the loop is still running.
355 bool did_work = delegate_->DoIdleWork();
356 if (did_work) {
357 CFRunLoopSourceSignal(idle_work_source_);
358 }
360 return did_work;
361 }
363 // Called from the run loop.
364 // static
365 void MessagePumpCFRunLoopBase::RunNestingDeferredWorkSource(void* info) {
366 MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info);
367 self->RunNestingDeferredWork();
368 }
370 // Called by MessagePumpCFRunLoopBase::RunNestingDeferredWorkSource.
371 bool MessagePumpCFRunLoopBase::RunNestingDeferredWork() {
372 if (!delegate_) {
373 // This point can be reached with a NULL delegate_ if Run is not on the
374 // stack but foreign code is spinning the CFRunLoop. There's no sense in
375 // attempting to do any work or signalling the work sources because
376 // without a delegate, work is not possible.
377 return false;
378 }
380 // Immediately try work in priority order.
381 if (!RunWork()) {
382 if (!RunDelayedWork()) {
383 if (!RunIdleWork()) {
384 return false;
385 }
386 } else {
387 // There was no work, and delayed work was done. Arrange for the loop
388 // to try non-nestable idle work on a subsequent pass.
389 CFRunLoopSourceSignal(idle_work_source_);
390 }
391 } else {
392 // Work was done. Arrange for the loop to try non-nestable delayed and
393 // idle work on a subsequent pass.
394 CFRunLoopSourceSignal(delayed_work_source_);
395 CFRunLoopSourceSignal(idle_work_source_);
396 }
398 return true;
399 }
401 // Called before the run loop goes to sleep or exits, or processes sources.
402 void MessagePumpCFRunLoopBase::MaybeScheduleNestingDeferredWork() {
403 // deepest_nesting_level_ is set as run loops are entered. If the deepest
404 // level encountered is deeper than the current level, a nested loop
405 // (relative to the current level) ran since the last time nesting-deferred
406 // work was scheduled. When that situation is encountered, schedule
407 // nesting-deferred work in case any work was deferred because nested work
408 // was disallowed.
409 if (deepest_nesting_level_ > nesting_level_) {
410 deepest_nesting_level_ = nesting_level_;
411 CFRunLoopSourceSignal(nesting_deferred_work_source_);
412 }
413 }
415 // Called from the run loop.
416 // static
417 void MessagePumpCFRunLoopBase::PreWaitObserver(CFRunLoopObserverRef observer,
418 CFRunLoopActivity activity,
419 void* info) {
420 MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info);
422 // Attempt to do some idle work before going to sleep.
423 self->RunIdleWork();
425 // The run loop is about to go to sleep. If any of the work done since it
426 // started or woke up resulted in a nested run loop running,
427 // nesting-deferred work may have accumulated. Schedule it for processing
428 // if appropriate.
429 self->MaybeScheduleNestingDeferredWork();
430 }
432 // Called from the run loop.
433 // static
434 void MessagePumpCFRunLoopBase::PreSourceObserver(CFRunLoopObserverRef observer,
435 CFRunLoopActivity activity,
436 void* info) {
437 MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info);
439 // The run loop has reached the top of the loop and is about to begin
440 // processing sources. If the last iteration of the loop at this nesting
441 // level did not sleep or exit, nesting-deferred work may have accumulated
442 // if a nested loop ran. Schedule nesting-deferred work for processing if
443 // appropriate.
444 self->MaybeScheduleNestingDeferredWork();
445 }
447 // Called from the run loop.
448 // static
449 void MessagePumpCFRunLoopBase::EnterExitObserver(CFRunLoopObserverRef observer,
450 CFRunLoopActivity activity,
451 void* info) {
452 MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info);
454 switch (activity) {
455 case kCFRunLoopEntry:
456 ++self->nesting_level_;
457 if (self->nesting_level_ > self->deepest_nesting_level_) {
458 self->deepest_nesting_level_ = self->nesting_level_;
459 }
460 break;
462 case kCFRunLoopExit:
463 // Not all run loops go to sleep. If a run loop is stopped before it
464 // goes to sleep due to a CFRunLoopStop call, or if the timeout passed
465 // to CFRunLoopRunInMode expires, the run loop may proceed directly from
466 // handling sources to exiting without any sleep. This most commonly
467 // occurs when CFRunLoopRunInMode is passed a timeout of 0, causing it
468 // to make a single pass through the loop and exit without sleep. Some
469 // native loops use CFRunLoop in this way. Because PreWaitObserver will
470 // not be called in these case, MaybeScheduleNestingDeferredWork needs
471 // to be called here, as the run loop exits.
472 //
473 // MaybeScheduleNestingDeferredWork consults self->nesting_level_
474 // to determine whether to schedule nesting-deferred work. It expects
475 // the nesting level to be set to the depth of the loop that is going
476 // to sleep or exiting. It must be called before decrementing the
477 // value so that the value still corresponds to the level of the exiting
478 // loop.
479 self->MaybeScheduleNestingDeferredWork();
480 --self->nesting_level_;
481 break;
483 default:
484 break;
485 }
487 self->EnterExitRunLoop(activity);
488 }
490 // Called from the run loop.
491 // static
492 void MessagePumpCFRunLoopBase::PowerStateNotification(void* info,
493 io_service_t service,
494 uint32_t message_type,
495 void* message_argument) {
496 // CFRunLoopTimer (NSTimer) is scheduled in terms of CFAbsoluteTime, which
497 // measures the number of seconds since 2001-01-01 00:00:00.0 Z. It is
498 // implemented in terms of kernel ticks, as in mach_absolute_time. While an
499 // offset and scale factor can be applied to convert between the two time
500 // bases at any time after boot, the kernel clock stops while the system is
501 // asleep, altering the offset. (The offset will also change when the
502 // real-time clock is adjusted.) CFRunLoopTimers are not readjusted to take
503 // this into account when the system wakes up, so any timers that were
504 // pending while the system was asleep will be delayed by the sleep
505 // duration.
506 //
507 // The MessagePump interface assumes that scheduled delayed work will be
508 // performed at the time ScheduleDelayedWork was asked to perform it. The
509 // delay caused by the CFRunLoopTimer not firing at the appropriate time
510 // results in a stall of queued delayed work when the system wakes up.
511 // With this limitation, scheduled work would not be performed until
512 // (system wake time + scheduled work time - system sleep time), while it
513 // would be expected to be performed at (scheduled work time).
514 //
515 // To work around this problem, when the system wakes up from sleep, if a
516 // delayed work timer is pending, it is rescheduled to fire at the original
517 // time that it was scheduled to fire.
518 //
519 // This mechanism is not resilient if the real-time clock does not maintain
520 // stable time while the system is sleeping, but it matches the behavior of
521 // the various other MessagePump implementations, and MessageLoop seems to
522 // be limited in the same way.
523 //
524 // References
525 // - Chris Kane, "NSTimer and deep sleep," cocoa-dev@lists.apple.com,
526 // http://lists.apple.com/archives/Cocoa-dev/2002/May/msg01547.html
527 // - Apple Technical Q&A QA1340, "Registering and unregistering for sleep
528 // and wake notifications,"
529 // http://developer.apple.com/mac/library/qa/qa2004/qa1340.html
530 // - Core Foundation source code, CF-550/CFRunLoop.c and CF-550/CFDate.c,
531 // http://www.opensource.apple.com/
533 MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info);
535 switch (message_type) {
536 case kIOMessageSystemWillPowerOn:
537 if (self->delayed_work_fire_time_ != kCFTimeIntervalMax) {
538 CFRunLoopTimerSetNextFireDate(self->delayed_work_timer_,
539 self->delayed_work_fire_time_);
540 }
541 break;
543 case kIOMessageSystemWillSleep:
544 case kIOMessageCanSystemSleep:
545 // The system will wait for 30 seconds before entering sleep if neither
546 // IOAllowPowerChange nor IOCancelPowerChange are called. That would be
547 // pretty antisocial.
548 IOAllowPowerChange(self->root_power_domain_,
549 reinterpret_cast<long>(message_argument));
550 break;
552 default:
553 break;
554 }
555 }
557 // Called by MessagePumpCFRunLoopBase::EnterExitRunLoop. The default
558 // implementation is a no-op.
559 void MessagePumpCFRunLoopBase::EnterExitRunLoop(CFRunLoopActivity activity) {
560 }
562 // Base version returns a standard NSAutoreleasePool.
563 NSAutoreleasePool* MessagePumpCFRunLoopBase::CreateAutoreleasePool() {
564 return [[NSAutoreleasePool alloc] init];
565 }
567 MessagePumpCFRunLoop::MessagePumpCFRunLoop()
568 : quit_pending_(false) {
569 }
571 // Called by MessagePumpCFRunLoopBase::DoRun. If other CFRunLoopRun loops were
572 // running lower on the run loop thread's stack when this object was created,
573 // the same number of CFRunLoopRun loops must be running for the outermost call
574 // to Run. Run/DoRun are reentrant after that point.
575 void MessagePumpCFRunLoop::DoRun(Delegate* delegate) {
576 // This is completely identical to calling CFRunLoopRun(), except autorelease
577 // pool management is introduced.
578 int result;
579 do {
580 MessagePumpScopedAutoreleasePool autorelease_pool(this);
581 result = CFRunLoopRunInMode(kCFRunLoopDefaultMode,
582 kCFTimeIntervalMax,
583 false);
584 } while (result != kCFRunLoopRunStopped && result != kCFRunLoopRunFinished);
585 }
587 // Must be called on the run loop thread.
588 void MessagePumpCFRunLoop::Quit() {
589 // Stop the innermost run loop managed by this MessagePumpCFRunLoop object.
590 if (nesting_level() == run_nesting_level()) {
591 // This object is running the innermost loop, just stop it.
592 CFRunLoopStop(run_loop());
593 } else {
594 // There's another loop running inside the loop managed by this object.
595 // In other words, someone else called CFRunLoopRunInMode on the same
596 // thread, deeper on the stack than the deepest Run call. Don't preempt
597 // other run loops, just mark this object to quit the innermost Run as
598 // soon as the other inner loops not managed by Run are done.
599 quit_pending_ = true;
600 }
601 }
603 // Called by MessagePumpCFRunLoopBase::EnterExitObserver.
604 void MessagePumpCFRunLoop::EnterExitRunLoop(CFRunLoopActivity activity) {
605 if (activity == kCFRunLoopExit &&
606 nesting_level() == run_nesting_level() &&
607 quit_pending_) {
608 // Quit was called while loops other than those managed by this object
609 // were running further inside a run loop managed by this object. Now
610 // that all unmanaged inner run loops are gone, stop the loop running
611 // just inside Run.
612 CFRunLoopStop(run_loop());
613 quit_pending_ = false;
614 }
615 }
617 MessagePumpNSRunLoop::MessagePumpNSRunLoop()
618 : keep_running_(true) {
619 CFRunLoopSourceContext source_context = CFRunLoopSourceContext();
620 source_context.perform = NoOp;
621 quit_source_ = CFRunLoopSourceCreate(NULL, // allocator
622 0, // priority
623 &source_context);
624 CFRunLoopAddSource(run_loop(), quit_source_, kCFRunLoopCommonModes);
625 }
627 MessagePumpNSRunLoop::~MessagePumpNSRunLoop() {
628 CFRunLoopRemoveSource(run_loop(), quit_source_, kCFRunLoopCommonModes);
629 CFRelease(quit_source_);
630 }
632 void MessagePumpNSRunLoop::DoRun(Delegate* delegate) {
633 while (keep_running_) {
634 // NSRunLoop manages autorelease pools itself.
635 [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode
636 beforeDate:[NSDate distantFuture]];
637 }
639 keep_running_ = true;
640 }
642 void MessagePumpNSRunLoop::Quit() {
643 keep_running_ = false;
644 CFRunLoopSourceSignal(quit_source_);
645 CFRunLoopWakeUp(run_loop());
646 }
648 MessagePumpNSApplication::MessagePumpNSApplication()
649 : keep_running_(true),
650 running_own_loop_(false) {
651 }
653 void MessagePumpNSApplication::DoRun(Delegate* delegate) {
654 bool last_running_own_loop_ = running_own_loop_;
656 // TODO(dmaclach): Get rid of this gratuitous sharedApplication.
657 // Tests should be setting up their applications on their own.
658 [CrApplication sharedApplication];
660 if (![NSApp isRunning]) {
661 running_own_loop_ = false;
662 // NSApplication manages autorelease pools itself when run this way.
663 [NSApp run];
664 } else {
665 running_own_loop_ = true;
666 NSDate* distant_future = [NSDate distantFuture];
667 while (keep_running_) {
668 MessagePumpScopedAutoreleasePool autorelease_pool(this);
669 NSEvent* event = [NSApp nextEventMatchingMask:NSAnyEventMask
670 untilDate:distant_future
671 inMode:NSDefaultRunLoopMode
672 dequeue:YES];
673 if (event) {
674 [NSApp sendEvent:event];
675 }
676 }
677 keep_running_ = true;
678 }
680 running_own_loop_ = last_running_own_loop_;
681 }
683 void MessagePumpNSApplication::Quit() {
684 if (!running_own_loop_) {
685 [NSApp stop:nil];
686 } else {
687 keep_running_ = false;
688 }
690 // Send a fake event to wake the loop up.
691 [NSApp postEvent:[NSEvent otherEventWithType:NSApplicationDefined
692 location:NSMakePoint(0, 0)
693 modifierFlags:0
694 timestamp:0
695 windowNumber:0
696 context:NULL
697 subtype:0
698 data1:0
699 data2:0]
700 atStart:NO];
701 }
703 // Prevents an autorelease pool from being created if the app is in the midst of
704 // handling a UI event because various parts of AppKit depend on objects that
705 // are created while handling a UI event to be autoreleased in the event loop.
706 // An example of this is NSWindowController. When a window with a window
707 // controller is closed it goes through a stack like this:
708 // (Several stack frames elided for clarity)
709 //
710 // #0 [NSWindowController autorelease]
711 // #1 DoAClose
712 // #2 MessagePumpCFRunLoopBase::DoWork()
713 // #3 [NSRunLoop run]
714 // #4 [NSButton performClick:]
715 // #5 [NSWindow sendEvent:]
716 // #6 [NSApp sendEvent:]
717 // #7 [NSApp run]
718 //
719 // -performClick: spins a nested run loop. If the pool created in DoWork was a
720 // standard NSAutoreleasePool, it would release the objects that were
721 // autoreleased into it once DoWork released it. This would cause the window
722 // controller, which autoreleased itself in frame #0, to release itself, and
723 // possibly free itself. Unfortunately this window controller controls the
724 // window in frame #5. When the stack is unwound to frame #5, the window would
725 // no longer exists and crashes may occur. Apple gets around this by never
726 // releasing the pool it creates in frame #4, and letting frame #7 clean it up
727 // when it cleans up the pool that wraps frame #7. When an autorelease pool is
728 // released it releases all other pools that were created after it on the
729 // autorelease pool stack.
730 //
731 // CrApplication is responsible for setting handlingSendEvent to true just
732 // before it sends the event throught the event handling mechanism, and
733 // returning it to its previous value once the event has been sent.
734 NSAutoreleasePool* MessagePumpNSApplication::CreateAutoreleasePool() {
735 NSAutoreleasePool* pool = nil;
736 DCHECK([NSApp isKindOfClass:[CrApplication class]]);
737 if (![static_cast<CrApplication*>(NSApp) isHandlingSendEvent]) {
738 pool = MessagePumpCFRunLoopBase::CreateAutoreleasePool();
739 }
740 return pool;
741 }
743 // static
744 MessagePump* MessagePumpMac::Create() {
745 if ([NSThread isMainThread]) {
746 return new MessagePumpNSApplication;
747 }
749 return new MessagePumpNSRunLoop;
750 }
752 } // namespace base