OLD | NEW |
---|---|
1 // Copyright (c) 2008 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "base/message_pump_mac.h" | 5 #include "base/message_pump_mac.h" |
6 | 6 |
7 #import <AppKit/AppKit.h> | 7 #import <AppKit/AppKit.h> |
8 #import <Foundation/Foundation.h> | 8 #import <Foundation/Foundation.h> |
9 #include <IOKit/IOMessage.h> | 9 #include <IOKit/IOMessage.h> |
10 #include <IOKit/pwr_mgt/IOPMLib.h> | 10 #include <IOKit/pwr_mgt/IOPMLib.h> |
11 | 11 |
12 #include <limits> | 12 #include <limits> |
13 | 13 |
14 #include "base/scoped_nsautorelease_pool.h" | 14 #import "base/chrome_application_mac.h" |
15 #include "base/logging.h" | |
15 #include "base/time.h" | 16 #include "base/time.h" |
16 | 17 |
17 namespace { | 18 namespace { |
18 | 19 |
19 void NoOp(void* info) { | 20 void NoOp(void* info) { |
20 } | 21 } |
21 | 22 |
22 const CFTimeInterval kCFTimeIntervalMax = | 23 const CFTimeInterval kCFTimeIntervalMax = |
23 std::numeric_limits<CFTimeInterval>::max(); | 24 std::numeric_limits<CFTimeInterval>::max(); |
24 | 25 |
25 } // namespace | 26 } // namespace |
26 | 27 |
27 namespace base { | 28 namespace base { |
28 | 29 |
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()) { | |
Mark Mentovai
2009/11/05 20:28:40
Final really tiny minor insignificant nit that you
| |
37 } | |
38 ~MessagePumpScopedAutoreleasePool() { | |
Scott Hess - ex-Googler
2009/11/05 20:36:12
Nit: indentation.
| |
39 [pool_ drain]; | |
40 } | |
41 | |
42 private: | |
43 NSAutoreleasePool* pool_; | |
44 DISALLOW_COPY_AND_ASSIGN(MessagePumpScopedAutoreleasePool); | |
45 }; | |
46 | |
29 // Must be called on the run loop thread. | 47 // Must be called on the run loop thread. |
30 MessagePumpCFRunLoopBase::MessagePumpCFRunLoopBase() | 48 MessagePumpCFRunLoopBase::MessagePumpCFRunLoopBase() |
31 : delegate_(NULL), | 49 : delegate_(NULL), |
32 delayed_work_fire_time_(kCFTimeIntervalMax), | 50 delayed_work_fire_time_(kCFTimeIntervalMax), |
33 nesting_level_(0), | 51 nesting_level_(0), |
34 run_nesting_level_(0), | 52 run_nesting_level_(0), |
35 deepest_nesting_level_(0), | 53 deepest_nesting_level_(0), |
36 delegateless_work_(false), | 54 delegateless_work_(false), |
37 delegateless_delayed_work_(false), | 55 delegateless_delayed_work_(false), |
38 delegateless_idle_work_(false) { | 56 delegateless_idle_work_(false) { |
(...skipping 218 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
257 // This point can be reached with a NULL delegate_ if Run is not on the | 275 // This point can be reached with a NULL delegate_ if Run is not on the |
258 // stack but foreign code is spinning the CFRunLoop. Arrange to come back | 276 // stack but foreign code is spinning the CFRunLoop. Arrange to come back |
259 // here when a delegate is available. | 277 // here when a delegate is available. |
260 delegateless_work_ = true; | 278 delegateless_work_ = true; |
261 return false; | 279 return false; |
262 } | 280 } |
263 | 281 |
264 // The NSApplication-based run loop only drains the autorelease pool at each | 282 // The NSApplication-based run loop only drains the autorelease pool at each |
265 // UI event (NSEvent). The autorelease pool is not drained for each | 283 // UI event (NSEvent). The autorelease pool is not drained for each |
266 // CFRunLoopSource target that's run. Use a local pool for any autoreleased | 284 // CFRunLoopSource target that's run. Use a local pool for any autoreleased |
267 // objects to ensure they're released promptly even in the absence of UI | 285 // objects if the app is not currently handling a UI event to ensure they're |
268 // events. | 286 // released promptly even in the absence of UI events. |
269 ScopedNSAutoreleasePool autorelease_pool; | 287 MessagePumpScopedAutoreleasePool autorelease_pool(this); |
270 | 288 |
271 // Call DoWork once, and if something was done, arrange to come back here | 289 // Call DoWork once, and if something was done, arrange to come back here |
272 // again as long as the loop is still running. | 290 // again as long as the loop is still running. |
273 bool did_work = delegate_->DoWork(); | 291 bool did_work = delegate_->DoWork(); |
274 if (did_work) { | 292 if (did_work) { |
275 CFRunLoopSourceSignal(work_source_); | 293 CFRunLoopSourceSignal(work_source_); |
276 } | 294 } |
277 | 295 |
278 return did_work; | 296 return did_work; |
279 } | 297 } |
(...skipping 11 matching lines...) Expand all Loading... | |
291 // This point can be reached with a NULL delegate_ if Run is not on the | 309 // This point can be reached with a NULL delegate_ if Run is not on the |
292 // stack but foreign code is spinning the CFRunLoop. Arrange to come back | 310 // stack but foreign code is spinning the CFRunLoop. Arrange to come back |
293 // here when a delegate is available. | 311 // here when a delegate is available. |
294 delegateless_delayed_work_ = true; | 312 delegateless_delayed_work_ = true; |
295 return false; | 313 return false; |
296 } | 314 } |
297 | 315 |
298 // The NSApplication-based run loop only drains the autorelease pool at each | 316 // The NSApplication-based run loop only drains the autorelease pool at each |
299 // UI event (NSEvent). The autorelease pool is not drained for each | 317 // UI event (NSEvent). The autorelease pool is not drained for each |
300 // CFRunLoopSource target that's run. Use a local pool for any autoreleased | 318 // CFRunLoopSource target that's run. Use a local pool for any autoreleased |
301 // objects to ensure they're released promptly even in the absence of UI | 319 // objects if the app is not currently handling a UI event to ensure they're |
302 // events. | 320 // released promptly even in the absence of UI events. |
303 ScopedNSAutoreleasePool autorelease_pool; | 321 MessagePumpScopedAutoreleasePool autorelease_pool(this); |
304 | 322 |
305 Time next_time; | 323 Time next_time; |
306 delegate_->DoDelayedWork(&next_time); | 324 delegate_->DoDelayedWork(&next_time); |
307 | 325 |
308 bool more_work = !next_time.is_null(); | 326 bool more_work = !next_time.is_null(); |
309 if (more_work) { | 327 if (more_work) { |
310 TimeDelta delay = next_time - Time::Now(); | 328 TimeDelta delay = next_time - Time::Now(); |
311 if (delay > TimeDelta()) { | 329 if (delay > TimeDelta()) { |
312 // There's more delayed work to be done in the future. | 330 // There's more delayed work to be done in the future. |
313 ScheduleDelayedWork(next_time); | 331 ScheduleDelayedWork(next_time); |
(...skipping 21 matching lines...) Expand all Loading... | |
335 // This point can be reached with a NULL delegate_ if Run is not on the | 353 // This point can be reached with a NULL delegate_ if Run is not on the |
336 // stack but foreign code is spinning the CFRunLoop. Arrange to come back | 354 // stack but foreign code is spinning the CFRunLoop. Arrange to come back |
337 // here when a delegate is available. | 355 // here when a delegate is available. |
338 delegateless_idle_work_ = true; | 356 delegateless_idle_work_ = true; |
339 return false; | 357 return false; |
340 } | 358 } |
341 | 359 |
342 // The NSApplication-based run loop only drains the autorelease pool at each | 360 // The NSApplication-based run loop only drains the autorelease pool at each |
343 // UI event (NSEvent). The autorelease pool is not drained for each | 361 // UI event (NSEvent). The autorelease pool is not drained for each |
344 // CFRunLoopSource target that's run. Use a local pool for any autoreleased | 362 // CFRunLoopSource target that's run. Use a local pool for any autoreleased |
345 // objects to ensure they're released promptly even in the absence of UI | 363 // objects if the app is not currently handling a UI event to ensure they're |
346 // events. | 364 // released promptly even in the absence of UI events. |
347 ScopedNSAutoreleasePool autorelease_pool; | 365 MessagePumpScopedAutoreleasePool autorelease_pool(this); |
348 | 366 |
349 // Call DoIdleWork once, and if something was done, arrange to come back here | 367 // Call DoIdleWork once, and if something was done, arrange to come back here |
350 // again as long as the loop is still running. | 368 // again as long as the loop is still running. |
351 bool did_work = delegate_->DoIdleWork(); | 369 bool did_work = delegate_->DoIdleWork(); |
352 if (did_work) { | 370 if (did_work) { |
353 CFRunLoopSourceSignal(idle_work_source_); | 371 CFRunLoopSourceSignal(idle_work_source_); |
354 } | 372 } |
355 | 373 |
356 return did_work; | 374 return did_work; |
357 } | 375 } |
(...skipping 190 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
548 default: | 566 default: |
549 break; | 567 break; |
550 } | 568 } |
551 } | 569 } |
552 | 570 |
553 // Called by MessagePumpCFRunLoopBase::EnterExitRunLoop. The default | 571 // Called by MessagePumpCFRunLoopBase::EnterExitRunLoop. The default |
554 // implementation is a no-op. | 572 // implementation is a no-op. |
555 void MessagePumpCFRunLoopBase::EnterExitRunLoop(CFRunLoopActivity activity) { | 573 void MessagePumpCFRunLoopBase::EnterExitRunLoop(CFRunLoopActivity activity) { |
556 } | 574 } |
557 | 575 |
576 // Base version returns a standard NSAutoreleasePool. | |
577 NSAutoreleasePool* MessagePumpCFRunLoopBase::CreateAutoreleasePool() { | |
578 return [[NSAutoreleasePool alloc] init]; | |
579 } | |
580 | |
558 MessagePumpCFRunLoop::MessagePumpCFRunLoop() | 581 MessagePumpCFRunLoop::MessagePumpCFRunLoop() |
559 : quit_pending_(false) { | 582 : quit_pending_(false) { |
560 } | 583 } |
561 | 584 |
562 // Called by MessagePumpCFRunLoopBase::DoRun. If other CFRunLoopRun loops were | 585 // Called by MessagePumpCFRunLoopBase::DoRun. If other CFRunLoopRun loops were |
563 // running lower on the run loop thread's stack when this object was created, | 586 // running lower on the run loop thread's stack when this object was created, |
564 // the same number of CFRunLoopRun loops must be running for the outermost call | 587 // the same number of CFRunLoopRun loops must be running for the outermost call |
565 // to Run. Run/DoRun are reentrant after that point. | 588 // to Run. Run/DoRun are reentrant after that point. |
566 void MessagePumpCFRunLoop::DoRun(Delegate* delegate) { | 589 void MessagePumpCFRunLoop::DoRun(Delegate* delegate) { |
567 // This is completely identical to calling CFRunLoopRun(), except autorelease | 590 // This is completely identical to calling CFRunLoopRun(), except autorelease |
568 // pool management is introduced. | 591 // pool management is introduced. |
569 int result; | 592 int result; |
570 do { | 593 do { |
571 ScopedNSAutoreleasePool autorelease_pool; | 594 MessagePumpScopedAutoreleasePool autorelease_pool(this); |
572 result = CFRunLoopRunInMode(kCFRunLoopDefaultMode, | 595 result = CFRunLoopRunInMode(kCFRunLoopDefaultMode, |
573 kCFTimeIntervalMax, | 596 kCFTimeIntervalMax, |
574 false); | 597 false); |
575 } while (result != kCFRunLoopRunStopped && result != kCFRunLoopRunFinished); | 598 } while (result != kCFRunLoopRunStopped && result != kCFRunLoopRunFinished); |
576 } | 599 } |
577 | 600 |
578 // Must be called on the run loop thread. | 601 // Must be called on the run loop thread. |
579 void MessagePumpCFRunLoop::Quit() { | 602 void MessagePumpCFRunLoop::Quit() { |
580 // Stop the innermost run loop managed by this MessagePumpCFRunLoop object. | 603 // Stop the innermost run loop managed by this MessagePumpCFRunLoop object. |
581 if (nesting_level() == run_nesting_level()) { | 604 if (nesting_level() == run_nesting_level()) { |
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
637 } | 660 } |
638 | 661 |
639 MessagePumpNSApplication::MessagePumpNSApplication() | 662 MessagePumpNSApplication::MessagePumpNSApplication() |
640 : keep_running_(true), | 663 : keep_running_(true), |
641 running_own_loop_(false) { | 664 running_own_loop_(false) { |
642 } | 665 } |
643 | 666 |
644 void MessagePumpNSApplication::DoRun(Delegate* delegate) { | 667 void MessagePumpNSApplication::DoRun(Delegate* delegate) { |
645 bool last_running_own_loop_ = running_own_loop_; | 668 bool last_running_own_loop_ = running_own_loop_; |
646 | 669 |
647 [NSApplication sharedApplication]; | 670 // TODO(dmaclach): Get rid of this gratuitous sharedApplication. |
671 // Tests should be setting up their applications on their own. | |
672 [CrApplication sharedApplication]; | |
648 | 673 |
649 if (![NSApp isRunning]) { | 674 if (![NSApp isRunning]) { |
650 running_own_loop_ = false; | 675 running_own_loop_ = false; |
651 // NSApplication manages autorelease pools itself when run this way. | 676 // NSApplication manages autorelease pools itself when run this way. |
652 [NSApp run]; | 677 [NSApp run]; |
653 } else { | 678 } else { |
654 running_own_loop_ = true; | 679 running_own_loop_ = true; |
655 NSDate* distant_future = [NSDate distantFuture]; | 680 NSDate* distant_future = [NSDate distantFuture]; |
656 while (keep_running_) { | 681 while (keep_running_) { |
657 ScopedNSAutoreleasePool autorelease_pool; | 682 MessagePumpScopedAutoreleasePool autorelease_pool(this); |
658 NSEvent* event = [NSApp nextEventMatchingMask:NSAnyEventMask | 683 NSEvent* event = [NSApp nextEventMatchingMask:NSAnyEventMask |
659 untilDate:distant_future | 684 untilDate:distant_future |
660 inMode:NSDefaultRunLoopMode | 685 inMode:NSDefaultRunLoopMode |
661 dequeue:YES]; | 686 dequeue:YES]; |
662 if (event) { | 687 if (event) { |
663 [NSApp sendEvent:event]; | 688 [NSApp sendEvent:event]; |
664 } | 689 } |
665 } | 690 } |
666 keep_running_ = true; | 691 keep_running_ = true; |
667 } | 692 } |
(...skipping 14 matching lines...) Expand all Loading... | |
682 modifierFlags:0 | 707 modifierFlags:0 |
683 timestamp:0 | 708 timestamp:0 |
684 windowNumber:0 | 709 windowNumber:0 |
685 context:NULL | 710 context:NULL |
686 subtype:0 | 711 subtype:0 |
687 data1:0 | 712 data1:0 |
688 data2:0] | 713 data2:0] |
689 atStart:NO]; | 714 atStart:NO]; |
690 } | 715 } |
691 | 716 |
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) | |
723 // | |
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] | |
732 // | |
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. | |
744 // | |
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; | |
755 } | |
756 | |
692 // static | 757 // static |
693 MessagePump* MessagePumpMac::Create() { | 758 MessagePump* MessagePumpMac::Create() { |
694 if ([NSThread isMainThread]) { | 759 if ([NSThread isMainThread]) { |
695 return new MessagePumpNSApplication; | 760 return new MessagePumpNSApplication; |
696 } | 761 } |
697 | 762 |
698 return new MessagePumpNSRunLoop; | 763 return new MessagePumpNSRunLoop; |
699 } | 764 } |
700 | 765 |
701 } // namespace base | 766 } // namespace base |
OLD | NEW |