Chromium Code Reviews| 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 |