Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(95)

Side by Side Diff: base/message_pump_mac.mm

Issue 339095: DeferredAutoreleasePool didn't work on Snow Leopard (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: Created 11 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « base/message_pump_mac.h ('k') | chrome/browser/cocoa/browser_window_controller.mm » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2009 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/logging.h"
15 #include "base/scoped_nsautorelease_pool.h" 14 #include "base/scoped_nsautorelease_pool.h"
16 #include "base/scoped_nsobject.h"
17 #include "base/scoped_ptr.h"
18 #include "base/time.h" 15 #include "base/time.h"
19 16
20 // This pool passes the objects stored in it to the pool below it in the
21 // autorelease pool stack when -drain is called. See its implementation
22 // for all the gory details.
23 @interface DeferredAutoreleasePool : NSAutoreleasePool {
24 @private
25 NSMutableArray* deferredPool_; // Strong.
26 }
27 @end
28
29 // Informs the message pump that it has been drained.
30 @interface MessagePumpNSAppDeferredAutoReleasePool : DeferredAutoreleasePool {
31 @private
32 base::MessagePumpNSApplication* pump_; // Weak.
33 }
34
35 - (id)initWithPump:(base::MessagePumpNSApplication*)pump;
36 @end
37
38 namespace { 17 namespace {
39 18
40 void NoOp(void* info) { 19 void NoOp(void* info) {
41 } 20 }
42 21
43 const CFTimeInterval kCFTimeIntervalMax = 22 const CFTimeInterval kCFTimeIntervalMax =
44 std::numeric_limits<CFTimeInterval>::max(); 23 std::numeric_limits<CFTimeInterval>::max();
45 24
46 // Class for dealing with scoping autorelease pools when they need to be a
47 // special implementation of NSAutoreleasePool.
48 // This class is independant from ScopedNSAutoreleasePool because having
49 // ScopedNSAutoreleasePool(NSAutoreleasePool*) would just clutter
50 // ScopedNSAutoreleasePool's interface, potentially leading to misuse, and the
51 // case where using an externally created instance of a kind of
52 // NSAutoreleasePool is so rare.
53 class AutoreleasePoolOwner {
54 public:
55 explicit AutoreleasePoolOwner(NSAutoreleasePool *pool) : pool_(pool) {}
56 ~AutoreleasePoolOwner() {
57 [pool_ drain];
58 }
59 private:
60 NSAutoreleasePool *pool_;
61 DISALLOW_COPY_AND_ASSIGN(AutoreleasePoolOwner);
62 };
63
64 } // namespace 25 } // namespace
65 26
66 namespace base { 27 namespace base {
67 28
68 // Must be called on the run loop thread. 29 // Must be called on the run loop thread.
69 MessagePumpCFRunLoopBase::MessagePumpCFRunLoopBase() 30 MessagePumpCFRunLoopBase::MessagePumpCFRunLoopBase()
70 : delegate_(NULL), 31 : delegate_(NULL),
71 delayed_work_fire_time_(kCFTimeIntervalMax), 32 delayed_work_fire_time_(kCFTimeIntervalMax),
72 nesting_level_(0), 33 nesting_level_(0),
73 run_nesting_level_(0), 34 run_nesting_level_(0),
(...skipping 219 matching lines...) Expand 10 before | Expand all | Expand 10 after
293 // Called by MessagePumpCFRunLoopBase::RunWorkSource. 254 // Called by MessagePumpCFRunLoopBase::RunWorkSource.
294 bool MessagePumpCFRunLoopBase::RunWork() { 255 bool MessagePumpCFRunLoopBase::RunWork() {
295 if (!delegate_) { 256 if (!delegate_) {
296 // This point can be reached with a NULL delegate_ if Run is not on the 257 // This point can be reached with a NULL delegate_ if Run is not on the
297 // stack but foreign code is spinning the CFRunLoop. Arrange to come back 258 // stack but foreign code is spinning the CFRunLoop. Arrange to come back
298 // here when a delegate is available. 259 // here when a delegate is available.
299 delegateless_work_ = true; 260 delegateless_work_ = true;
300 return false; 261 return false;
301 } 262 }
302 263
303 AutoreleasePoolOwner pool(CreateAutoreleasePool()); 264 // The NSApplication-based run loop only drains the autorelease pool at each
265 // UI event (NSEvent). The autorelease pool is not drained for each
266 // 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
268 // events.
269 ScopedNSAutoreleasePool autorelease_pool;
304 270
305 // Call DoWork once, and if something was done, arrange to come back here 271 // Call DoWork once, and if something was done, arrange to come back here
306 // again as long as the loop is still running. 272 // again as long as the loop is still running.
307 bool did_work = delegate_->DoWork(); 273 bool did_work = delegate_->DoWork();
308 if (did_work) { 274 if (did_work) {
309 CFRunLoopSourceSignal(work_source_); 275 CFRunLoopSourceSignal(work_source_);
310 } 276 }
311 277
312 return did_work; 278 return did_work;
313 } 279 }
314 280
315 // Called from the run loop. 281 // Called from the run loop.
316 // static 282 // static
317 void MessagePumpCFRunLoopBase::RunDelayedWorkSource(void* info) { 283 void MessagePumpCFRunLoopBase::RunDelayedWorkSource(void* info) {
318 MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info); 284 MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info);
319 self->RunDelayedWork(); 285 self->RunDelayedWork();
320 } 286 }
321 287
322 // Called by MessagePumpCFRunLoopBase::RunDelayedWorkSource. 288 // Called by MessagePumpCFRunLoopBase::RunDelayedWorkSource.
323 bool MessagePumpCFRunLoopBase::RunDelayedWork() { 289 bool MessagePumpCFRunLoopBase::RunDelayedWork() {
324 if (!delegate_) { 290 if (!delegate_) {
325 // This point can be reached with a NULL delegate_ if Run is not on the 291 // This point can be reached with a NULL delegate_ if Run is not on the
326 // stack but foreign code is spinning the CFRunLoop. Arrange to come back 292 // stack but foreign code is spinning the CFRunLoop. Arrange to come back
327 // here when a delegate is available. 293 // here when a delegate is available.
328 delegateless_delayed_work_ = true; 294 delegateless_delayed_work_ = true;
329 return false; 295 return false;
330 } 296 }
331 297
332 AutoreleasePoolOwner pool(CreateAutoreleasePool()); 298 // The NSApplication-based run loop only drains the autorelease pool at each
299 // UI event (NSEvent). The autorelease pool is not drained for each
300 // 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
302 // events.
303 ScopedNSAutoreleasePool autorelease_pool;
333 304
334 Time next_time; 305 Time next_time;
335 delegate_->DoDelayedWork(&next_time); 306 delegate_->DoDelayedWork(&next_time);
336 307
337 bool more_work = !next_time.is_null(); 308 bool more_work = !next_time.is_null();
338 if (more_work) { 309 if (more_work) {
339 TimeDelta delay = next_time - Time::Now(); 310 TimeDelta delay = next_time - Time::Now();
340 if (delay > TimeDelta()) { 311 if (delay > TimeDelta()) {
341 // There's more delayed work to be done in the future. 312 // There's more delayed work to be done in the future.
342 ScheduleDelayedWork(next_time); 313 ScheduleDelayedWork(next_time);
(...skipping 18 matching lines...) Expand all
361 // Called by MessagePumpCFRunLoopBase::RunIdleWorkSource. 332 // Called by MessagePumpCFRunLoopBase::RunIdleWorkSource.
362 bool MessagePumpCFRunLoopBase::RunIdleWork() { 333 bool MessagePumpCFRunLoopBase::RunIdleWork() {
363 if (!delegate_) { 334 if (!delegate_) {
364 // This point can be reached with a NULL delegate_ if Run is not on the 335 // This point can be reached with a NULL delegate_ if Run is not on the
365 // stack but foreign code is spinning the CFRunLoop. Arrange to come back 336 // stack but foreign code is spinning the CFRunLoop. Arrange to come back
366 // here when a delegate is available. 337 // here when a delegate is available.
367 delegateless_idle_work_ = true; 338 delegateless_idle_work_ = true;
368 return false; 339 return false;
369 } 340 }
370 341
371 AutoreleasePoolOwner pool(CreateAutoreleasePool()); 342 // The NSApplication-based run loop only drains the autorelease pool at each
343 // UI event (NSEvent). The autorelease pool is not drained for each
344 // 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
346 // events.
347 ScopedNSAutoreleasePool autorelease_pool;
372 348
373 // Call DoIdleWork once, and if something was done, arrange to come back here 349 // Call DoIdleWork once, and if something was done, arrange to come back here
374 // again as long as the loop is still running. 350 // again as long as the loop is still running.
375 bool did_work = delegate_->DoIdleWork(); 351 bool did_work = delegate_->DoIdleWork();
376 if (did_work) { 352 if (did_work) {
377 CFRunLoopSourceSignal(idle_work_source_); 353 CFRunLoopSourceSignal(idle_work_source_);
378 } 354 }
379 355
380 return did_work; 356 return did_work;
381 } 357 }
(...skipping 190 matching lines...) Expand 10 before | Expand all | Expand 10 after
572 default: 548 default:
573 break; 549 break;
574 } 550 }
575 } 551 }
576 552
577 // Called by MessagePumpCFRunLoopBase::EnterExitRunLoop. The default 553 // Called by MessagePumpCFRunLoopBase::EnterExitRunLoop. The default
578 // implementation is a no-op. 554 // implementation is a no-op.
579 void MessagePumpCFRunLoopBase::EnterExitRunLoop(CFRunLoopActivity activity) { 555 void MessagePumpCFRunLoopBase::EnterExitRunLoop(CFRunLoopActivity activity) {
580 } 556 }
581 557
582 // Called by MessagePumpCFRunLoopBase::RunWork,
583 // MessagePumpCFRunLoopBase::RunDelayedWork and
584 // MessagePumpCFRunLoopBase::RunIdleWork. Default implementation is a standard
585 // NSAutoreleasePool that can be used for eash source as it fires.
586 NSAutoreleasePool* MessagePumpCFRunLoopBase::CreateAutoreleasePool() {
587 return [[NSAutoreleasePool alloc] init];
588 }
589
590 MessagePumpCFRunLoop::MessagePumpCFRunLoop() 558 MessagePumpCFRunLoop::MessagePumpCFRunLoop()
591 : quit_pending_(false) { 559 : quit_pending_(false) {
592 } 560 }
593 561
594 // Called by MessagePumpCFRunLoopBase::DoRun. If other CFRunLoopRun loops were 562 // Called by MessagePumpCFRunLoopBase::DoRun. If other CFRunLoopRun loops were
595 // running lower on the run loop thread's stack when this object was created, 563 // running lower on the run loop thread's stack when this object was created,
596 // the same number of CFRunLoopRun loops must be running for the outermost call 564 // the same number of CFRunLoopRun loops must be running for the outermost call
597 // to Run. Run/DoRun are reentrant after that point. 565 // to Run. Run/DoRun are reentrant after that point.
598 void MessagePumpCFRunLoop::DoRun(Delegate* delegate) { 566 void MessagePumpCFRunLoop::DoRun(Delegate* delegate) {
599 // This is completely identical to calling CFRunLoopRun(), except autorelease 567 // This is completely identical to calling CFRunLoopRun(), except autorelease
(...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after
663 } 631 }
664 632
665 void MessagePumpNSRunLoop::Quit() { 633 void MessagePumpNSRunLoop::Quit() {
666 keep_running_ = false; 634 keep_running_ = false;
667 CFRunLoopSourceSignal(quit_source_); 635 CFRunLoopSourceSignal(quit_source_);
668 CFRunLoopWakeUp(run_loop()); 636 CFRunLoopWakeUp(run_loop());
669 } 637 }
670 638
671 MessagePumpNSApplication::MessagePumpNSApplication() 639 MessagePumpNSApplication::MessagePumpNSApplication()
672 : keep_running_(true), 640 : keep_running_(true),
673 running_own_loop_(false), 641 running_own_loop_(false) {
674 needs_event_loop_wake_up_(false) {
675 } 642 }
676 643
677 void MessagePumpNSApplication::DoRun(Delegate* delegate) { 644 void MessagePumpNSApplication::DoRun(Delegate* delegate) {
678 bool last_running_own_loop_ = running_own_loop_; 645 bool last_running_own_loop_ = running_own_loop_;
679 646
680 [NSApplication sharedApplication]; 647 [NSApplication sharedApplication];
681 648
682 if (![NSApp isRunning]) { 649 if (![NSApp isRunning]) {
683 running_own_loop_ = false; 650 running_own_loop_ = false;
684 // NSApplication manages autorelease pools itself when run this way. 651 // NSApplication manages autorelease pools itself when run this way.
(...skipping 17 matching lines...) Expand all
702 running_own_loop_ = last_running_own_loop_; 669 running_own_loop_ = last_running_own_loop_;
703 } 670 }
704 671
705 void MessagePumpNSApplication::Quit() { 672 void MessagePumpNSApplication::Quit() {
706 if (!running_own_loop_) { 673 if (!running_own_loop_) {
707 [NSApp stop:nil]; 674 [NSApp stop:nil];
708 } else { 675 } else {
709 keep_running_ = false; 676 keep_running_ = false;
710 } 677 }
711 678
712 WakeUpEventLoop(); 679 // Send a fake event to wake the loop up.
713 } 680 [NSApp postEvent:[NSEvent otherEventWithType:NSApplicationDefined
714 681 location:NSMakePoint(0, 0)
715 // Called by MessagePumpCFRunLoopBase::EnterExitObserver. 682 modifierFlags:0
716 void MessagePumpNSApplication::EnterExitRunLoop(CFRunLoopActivity activity) { 683 timestamp:0
717 if (activity == kCFRunLoopExit && needs_event_loop_wake_up_) { 684 windowNumber:0
718 WakeUpEventLoop(); 685 context:NULL
719 needs_event_loop_wake_up_ = false; 686 subtype:0
720 } 687 data1:0
721 } 688 data2:0]
722 689 atStart:NO];
723 NSAutoreleasePool* MessagePumpNSApplication::CreateAutoreleasePool() {
724 // Return an instance of a special autorelease pool that deals
725 // correctly with nested autorelease pools in run loops. This is used by
726 // MessagePumpNSApplication instead of a standard NSAutoreleasePool
727 // because of how NSApplication interacts with CFRunLoops.
728 //
729 // A standard NSApplication event loop looks something like this:
730 //
731 // - (void)run {
732 // while ([self isRunning]) {
733 // NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
734 // NSEvent* event = [self nextEventMatchingMask:NSAnyEventMask
735 // untilDate:[NSDate distantFuture]
736 // inMode:NSDefaultRunLoopMode
737 // dequeue:YES];
738 // [self sendEvent:event];
739 // [self updateWindows];
740 // [pool release];
741 // }
742 // }
743 //
744 // Notice that the autorelease pool only gets cleaned up when an NSEvent gets
745 // returned by nextEventMatchingMask:untilDate:inMode:dequeue: as this is an
746 // an important point in getting objects released in a timely manner.
747 //
748 // Message pumps have a variety of CFRunLoopSources attached to their
749 // CFRunLoops. These sources can cause actions to run from within
750 // [NSApplication -nextEventMatchingMask:untilDate:inMode:dequeue:]. These
751 // actions can call into Objective C code when they fire and add things to the
752 // autorelease pool.
753 //
754 // The problem is that the sources don't necessarily cause
755 // an NSEvent to be generated and therefore the autorelease pool in
756 // [NSApplication run] is not necessarily released in a timely fashion. This
757 // potentially leaves a lot of objects that can be cleaned up sitting around
758 // taking up memory until something, such as an NSEvent, causes [NSApplication
759 // run] to release its pool.
760 //
761 // The easy answer would seem to be to wrap all of the calls from the
762 // sources in generic NSAutoreleasePools. Unfortunately, there is a nested
763 // case where some bit of code can run its own event loop causing the sources
764 // to fire. In this case, code below the secondary event loop on the stack may
765 // be depending on objects above the secondary event loop on the stack. These
766 // objects above the event loop may have autorelease called on them, and then
767 // they will be released when the source completes. The code below the
768 // secondary event loop will now be pointing to freed memory.
769 //
770 // eg:
771 // (Several stack frames elided for clarity)
772 //
773 // #0 [NSWindowController autorelease]
774 // #1 DoAClose
775 // #2 MessagePumpCFRunLoopBase::DoWork()
776 // #3 [NSRunLoop run]
777 // #4 [NSButton performClick:]
778 // #5 [NSWindow sendEvent:]
779 // #6 [NSApp sendEvent:]
780 // #7 [NSApp run]
781 //
782 // PerformClick: spins a nested run loop. If the pool created in DoWork is a
783 // standard NSAutoreleasePool, it will release the objects that have been
784 // autoreleased into it once DoWork releases it. This will cause the window
785 // controller, which autoreleased itself in frame #0, to release itself, and
786 // possibly free itself. Unfortunately this window controller controls the
787 // window in frame #5. When the stack is unwound to frame #5, window no longer
788 // exists and crashes can occur.
789 //
790 // The current solution is to have a deferred autorelease pool that collects
791 // objects and then passes them up to the next autorelease pool on the
792 // autorelease pool stack when -drain is called on it. This is handled by the
793 // code in DeferredAutoReleasePool. This moves the objects to the proper
794 // autorelease pool, but doesn't drain them from that pool.
795 //
796 // The problem then becomes getting the NSApplication event loop to spin to
797 // release its autorelease pool. This is done by sending an empty event
798 // through the event loop. This is handled by
799 // MessagePumpNSAppDeferredAutoReleasePool setting
800 // a flag in MessagePumpNSApplication, which is acted on by
801 // MessagePumpNSApplication::EnterExitRunLoop. The event is sent just as the
802 // MessagePumpNSApplication's run loop is exiting so that the event isn't
803 // unintentionally swallowed by one of the other sources spinning its own
804 // event loop.
805 return [[MessagePumpNSAppDeferredAutoReleasePool alloc] initWithPump:this];
806 }
807
808 void MessagePumpNSApplication::WakeUpEventLoop() {
809 // If there is already an event waiting in the queue, there is no need
810 // to post another one.
811 NSString* currentMode = [[NSRunLoop currentRunLoop] currentMode];
812 if (![NSApp nextEventMatchingMask:NSAnyEventMask
813 untilDate:nil
814 inMode:currentMode
815 dequeue:NO]) {
816 NSEvent* wakeUpEvent = [NSEvent otherEventWithType:NSApplicationDefined
817 location:NSZeroPoint
818 modifierFlags:0
819 timestamp:0
820 windowNumber:0
821 context:NULL
822 subtype:0
823 data1:0
824 data2:0];
825 [NSApp postEvent:wakeUpEvent atStart:NO];
826 }
827 } 690 }
828 691
829 // static 692 // static
830 MessagePump* MessagePumpMac::Create() { 693 MessagePump* MessagePumpMac::Create() {
831 if ([NSThread isMainThread]) { 694 if ([NSThread isMainThread]) {
832 return new MessagePumpNSApplication; 695 return new MessagePumpNSApplication;
833 } 696 }
834 697
835 return new MessagePumpNSRunLoop; 698 return new MessagePumpNSRunLoop;
836 } 699 }
837 700
838 // A trampoline to get around problem where ObjC classes/methods cannot be
839 // friends of C++ classes.
840 void SetNeedsEventLoopWakeUpTrue(MessagePumpNSApplication* pump) {
841 pump->set_needs_event_loop_wake_up_true();
842 }
843
844 } // namespace base 701 } // namespace base
845
846 @implementation DeferredAutoreleasePool
847
848 - (void)addObject:(id)anObject {
849 // If the GarbageCollector is running, none of this should be necessary
850 // and it can be tossed out.
851 DCHECK([NSGarbageCollector defaultCollector] == nil);
852 if (!deferredPool_) {
853 // Create an array to use as a store for autoreleased objects. Not created
854 // in init because the existence of the pool as a flag is used to
855 // determine if the event loop needs to be sent an event to cause
856 // the app to release its autorelease pool.
857 deferredPool_ = [[NSMutableArray alloc] init];
858 }
859
860 // Store the object in deferredPool_. Don't call [super addObject] because the
861 // retain/release is taken care of here.
862 // When -addObject is called the retain count on anObject is 'n'. When -drain
863 // is called on a normal NSAutoreleasePool the user expects the retain count
864 // of anObject to become 'n - 1'. When anObject is added to deferredPool_ the
865 // array calls -retain so the retain count becomes 'n + 1'. Release is
866 // called to return it to 'n' so that when anObject is eventually released
867 // by the NSAutoreleasePool above self in the autorelease pool stack the
868 // retain count will become 'n - 1' as expected.
869 [deferredPool_ addObject:anObject];
870 [anObject release];
871
872 // TODO(dmaclach): Is a call to
873 // NSRecordAllocationEvent(NSObjectAutoreleasedEvent, anObject) needed? I did
874 // some testing and set some breakpoints and nothing seems to call it.
875 }
876
877 - (void)drain {
878 // Store off the address of deferredPool_ because -[super drain] calls
879 // free on "self" and deferredPool_ is needed after drain. NSAutoreleasePools
880 // are interesting in that dealloc is never called on them and instead they
881 // override release and call free directly. This means that deferredPool_ is
882 // still alive, and would be leaked if we didn't release it, but the memory
883 // owned by self is dead so even though what deferredPool_ pointed to before
884 // calling -drain is still valid, the actual pointer value of deferredPool_
885 // is potentially garbage. By caching a copy of the pointer to deferredPool_
886 // on the stack before calling drain a good reference to deferredPool_ is
887 // available to be used post [super drain].
888 NSMutableArray* deferredPool = deferredPool_;
889 [super drain];
890
891 // This autorelease pool is now off the autorelease stack. Calling
892 // autorelease on deferredPool effectively transfers ownership of the
893 // objects in the array to the outer autorelease pool.
894 [deferredPool autorelease];
895 }
896
897 // Must call drain on DeferredAutoreleasePools.
898 - (oneway void)release {
899 CHECK(false);
900 }
901
902 @end
903
904 @implementation MessagePumpNSAppDeferredAutoReleasePool
905
906 - (id)initWithPump:(base::MessagePumpNSApplication*)pump {
907 if ((self = [super init])) {
908 pump_ = pump;
909 }
910 return self;
911 }
912
913 - (void)drain {
914 // Set a flag in pump_ so that it wakes up the event loop when exiting its
915 // run loop. Calling through a trampoline to get around problem where
916 // ObjC classes/methods cannot be friends of C++ classes.
917 base::SetNeedsEventLoopWakeUpTrue(pump_);
918 pump_ = nil;
919 [super drain];
920 }
921
922 @end
OLDNEW
« no previous file with comments | « base/message_pump_mac.h ('k') | chrome/browser/cocoa/browser_window_controller.mm » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698