| // Copyright (c) 2012 The Chromium Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| // The basis for all native run loops on the Mac is the CFRunLoop. It can be | |
| // used directly, it can be used as the driving force behind the similar | |
| // Foundation NSRunLoop, and it can be used to implement higher-level event | |
| // loops such as the NSApplication event loop. | |
| // | |
| // This file introduces a basic CFRunLoop-based implementation of the | |
| // MessagePump interface called CFRunLoopBase. CFRunLoopBase contains all | |
| // of the machinery necessary to dispatch events to a delegate, but does not | |
| // implement the specific run loop. Concrete subclasses must provide their | |
| // own DoRun and Quit implementations. | |
| // | |
| // A concrete subclass that just runs a CFRunLoop loop is provided in | |
| // MessagePumpCFRunLoop. For an NSRunLoop, the similar MessagePumpNSRunLoop | |
| // is provided. | |
| // | |
| // For the application's event loop, an implementation based on AppKit's | |
| // NSApplication event system is provided in MessagePumpNSApplication. | |
| // | |
| // Typically, MessagePumpNSApplication only makes sense on a Cocoa | |
| // application's main thread. If a CFRunLoop-based message pump is needed on | |
| // any other thread, one of the other concrete subclasses is preferable. | |
| // MessagePumpMac::Create is defined, which returns a new NSApplication-based | |
| // or NSRunLoop-based MessagePump subclass depending on which thread it is | |
| // called on. | |
| #ifndef BASE_MESSAGE_LOOP_MESSAGE_PUMP_MAC_H_ | |
| #define BASE_MESSAGE_LOOP_MESSAGE_PUMP_MAC_H_ | |
| #include "base/message_loop/message_pump.h" | |
| #include <CoreFoundation/CoreFoundation.h> | |
| #include "base/macros.h" | |
| #include "base/memory/weak_ptr.h" | |
| #include "base/message_loop/timer_slack.h" | |
| #include "build/build_config.h" | |
| #include "starboard/types.h" | |
| #if defined(__OBJC__) | |
| #if defined(OS_IOS) | |
| #import <Foundation/Foundation.h> | |
| #else | |
| #import <AppKit/AppKit.h> | |
| // Clients must subclass NSApplication and implement this protocol if they use | |
| // MessagePumpMac. | |
| @protocol CrAppProtocol | |
| // Must return true if -[NSApplication sendEvent:] is currently on the stack. | |
| // See the comment for |CreateAutoreleasePool()| in the cc file for why this is | |
| // necessary. | |
| - (BOOL)isHandlingSendEvent; | |
| @end | |
| #endif // !defined(OS_IOS) | |
| #endif // defined(__OBJC__) | |
| namespace base { | |
| class RunLoop; | |
| class TimeTicks; | |
| // AutoreleasePoolType is a proxy type for autorelease pools. Its definition | |
| // depends on the translation unit (TU) in which this header appears. In pure | |
| // C++ TUs, it is defined as a forward C++ class declaration (that is never | |
| // defined), because autorelease pools are an Objective-C concept. In Automatic | |
| // Reference Counting (ARC) Objective-C TUs, it is similarly defined as a | |
| // forward C++ class declaration, because clang will not allow the type | |
| // "NSAutoreleasePool" in such TUs. Finally, in Manual Retain Release (MRR) | |
| // Objective-C TUs, it is a type alias for NSAutoreleasePool. In all cases, a | |
| // method that takes or returns an NSAutoreleasePool* can use | |
| // AutoreleasePoolType* instead. | |
| #if !defined(__OBJC__) || __has_feature(objc_arc) | |
| class AutoreleasePoolType; | |
| #else // !defined(__OBJC__) || __has_feature(objc_arc) | |
| typedef NSAutoreleasePool AutoreleasePoolType; | |
| #endif // !defined(__OBJC__) || __has_feature(objc_arc) | |
| class BASE_EXPORT MessagePumpCFRunLoopBase : public MessagePump { | |
| public: | |
| // MessagePump: | |
| void Run(Delegate* delegate) override; | |
| void ScheduleWork() override; | |
| void ScheduleDelayedWork(const TimeTicks& delayed_work_time) override; | |
| void SetTimerSlack(TimerSlack timer_slack) override; | |
| protected: | |
| // Needs access to CreateAutoreleasePool. | |
| friend class MessagePumpScopedAutoreleasePool; | |
| friend class TestMessagePumpCFRunLoopBase; | |
| // Tasks will be pumped in the run loop modes described by | |
| // |initial_mode_mask|, which maps bits to the index of an internal array of | |
| // run loop mode identifiers. | |
| explicit MessagePumpCFRunLoopBase(int initial_mode_mask); | |
| ~MessagePumpCFRunLoopBase() override; | |
| // Subclasses should implement the work they need to do in MessagePump::Run | |
| // in the DoRun method. MessagePumpCFRunLoopBase::Run calls DoRun directly. | |
| // This arrangement is used because MessagePumpCFRunLoopBase needs to set | |
| // up and tear down things before and after the "meat" of DoRun. | |
| virtual void DoRun(Delegate* delegate) = 0; | |
| // Accessors for private data members to be used by subclasses. | |
| CFRunLoopRef run_loop() const { return run_loop_; } | |
| int nesting_level() const { return nesting_level_; } | |
| int run_nesting_level() const { return run_nesting_level_; } | |
| // Sets this pump's delegate. Signals the appropriate sources if | |
| // |delegateless_work_| is true. |delegate| can be NULL. | |
| void SetDelegate(Delegate* delegate); | |
| // Return an autorelease pool to wrap around any work being performed. | |
| // In some cases, CreateAutoreleasePool may return nil intentionally to | |
| // preventing an autorelease pool from being created, allowing any | |
| // objects autoreleased by work to fall into the current autorelease pool. | |
| virtual AutoreleasePoolType* CreateAutoreleasePool(); | |
| // Enable and disable entries in |enabled_modes_| to match |mode_mask|. | |
| void SetModeMask(int mode_mask); | |
| // Get the current mode mask from |enabled_modes_|. | |
| int GetModeMask() const; | |
| private: | |
| class ScopedModeEnabler; | |
| // The maximum number of run loop modes that can be monitored. | |
| static constexpr int kNumModes = 4; | |
| // Marking timers as invalid at the right time helps significantly reduce | |
| // power use (see the comment in RunDelayedWorkTimer()), however there is no | |
| // public API for doing so. CFRuntime.h states that CFRuntimeBase, upon which | |
| // the above timer invalidation functions are based, can change from release | |
| // to release and should not be accessed directly (this struct last changed at | |
| // least in 2008 in CF-476). | |
| // | |
| // This function uses private API to modify a test timer's valid state and | |
| // uses public API to confirm that the private API changed the right bit. | |
| static bool CanInvalidateCFRunLoopTimers(); | |
| // Sets a Core Foundation object's "invalid" bit to |valid|. Based on code | |
| // from CFRunLoop.c. | |
| static void ChromeCFRunLoopTimerSetValid(CFRunLoopTimerRef timer, bool valid); | |
| // Timer callback scheduled by ScheduleDelayedWork. This does not do any | |
| // work, but it signals work_source_ so that delayed work can be performed | |
| // within the appropriate priority constraints. | |
| static void RunDelayedWorkTimer(CFRunLoopTimerRef timer, void* info); | |
| // Perform highest-priority work. This is associated with work_source_ | |
| // signalled by ScheduleWork or RunDelayedWorkTimer. The static method calls | |
| // the instance method; the instance method returns true if it resignalled | |
| // work_source_ to be called again from the loop. | |
| static void RunWorkSource(void* info); | |
| bool RunWork(); | |
| // Perform idle-priority work. This is normally called by PreWaitObserver, | |
| // but is also associated with idle_work_source_. When this function | |
| // actually does perform idle work, it will resignal that source. The | |
| // static method calls the instance method; the instance method returns | |
| // true if idle work was done. | |
| static void RunIdleWorkSource(void* info); | |
| bool RunIdleWork(); | |
| // Perform work that may have been deferred because it was not runnable | |
| // within a nested run loop. This is associated with | |
| // nesting_deferred_work_source_ and is signalled by | |
| // MaybeScheduleNestingDeferredWork when returning from a nested loop, | |
| // so that an outer loop will be able to perform the necessary tasks if it | |
| // permits nestable tasks. | |
| static void RunNestingDeferredWorkSource(void* info); | |
| bool RunNestingDeferredWork(); | |
| // Schedules possible nesting-deferred work to be processed before the run | |
| // loop goes to sleep, exits, or begins processing sources at the top of its | |
| // loop. If this function detects that a nested loop had run since the | |
| // previous attempt to schedule nesting-deferred work, it will schedule a | |
| // call to RunNestingDeferredWorkSource. | |
| void MaybeScheduleNestingDeferredWork(); | |
| // Observer callback responsible for performing idle-priority work, before | |
| // the run loop goes to sleep. Associated with idle_work_observer_. | |
| static void PreWaitObserver(CFRunLoopObserverRef observer, | |
| CFRunLoopActivity activity, void* info); | |
| // Observer callback called before the run loop processes any sources. | |
| // Associated with pre_source_observer_. | |
| static void PreSourceObserver(CFRunLoopObserverRef observer, | |
| CFRunLoopActivity activity, void* info); | |
| // Observer callback called when the run loop starts and stops, at the | |
| // beginning and end of calls to CFRunLoopRun. This is used to maintain | |
| // nesting_level_. Associated with enter_exit_observer_. | |
| static void EnterExitObserver(CFRunLoopObserverRef observer, | |
| CFRunLoopActivity activity, void* info); | |
| // Called by EnterExitObserver after performing maintenance on nesting_level_. | |
| // This allows subclasses an opportunity to perform additional processing on | |
| // the basis of run loops starting and stopping. | |
| virtual void EnterExitRunLoop(CFRunLoopActivity activity); | |
| // The thread's run loop. | |
| CFRunLoopRef run_loop_; | |
| // The enabled modes. Posted tasks may run in any non-null entry. | |
| std::unique_ptr<ScopedModeEnabler> enabled_modes_[kNumModes]; | |
| // The timer, sources, and observers are described above alongside their | |
| // callbacks. | |
| CFRunLoopTimerRef delayed_work_timer_; | |
| CFRunLoopSourceRef work_source_; | |
| CFRunLoopSourceRef idle_work_source_; | |
| CFRunLoopSourceRef nesting_deferred_work_source_; | |
| CFRunLoopObserverRef pre_wait_observer_; | |
| CFRunLoopObserverRef pre_source_observer_; | |
| CFRunLoopObserverRef enter_exit_observer_; | |
| // (weak) Delegate passed as an argument to the innermost Run call. | |
| Delegate* delegate_; | |
| // The time that delayed_work_timer_ is scheduled to fire. This is tracked | |
| // independently of CFRunLoopTimerGetNextFireDate(delayed_work_timer_) | |
| // to be able to reset the timer properly after waking from system sleep. | |
| // See PowerStateNotification. | |
| CFAbsoluteTime delayed_work_fire_time_; | |
| base::TimerSlack timer_slack_; | |
| // The recursion depth of the currently-executing CFRunLoopRun loop on the | |
| // run loop's thread. 0 if no run loops are running inside of whatever scope | |
| // the object was created in. | |
| int nesting_level_; | |
| // The recursion depth (calculated in the same way as nesting_level_) of the | |
| // innermost executing CFRunLoopRun loop started by a call to Run. | |
| int run_nesting_level_; | |
| // The deepest (numerically highest) recursion depth encountered since the | |
| // most recent attempt to run nesting-deferred work. | |
| int deepest_nesting_level_; | |
| // "Delegateless" work flags are set when work is ready to be performed but | |
| // must wait until a delegate is available to process it. This can happen | |
| // when a MessagePumpCFRunLoopBase is instantiated and work arrives without | |
| // any call to Run on the stack. The Run method will check for delegateless | |
| // work on entry and redispatch it as needed once a delegate is available. | |
| bool delegateless_work_; | |
| bool delegateless_idle_work_; | |
| DISALLOW_COPY_AND_ASSIGN(MessagePumpCFRunLoopBase); | |
| }; | |
| class BASE_EXPORT MessagePumpCFRunLoop : public MessagePumpCFRunLoopBase { | |
| public: | |
| MessagePumpCFRunLoop(); | |
| ~MessagePumpCFRunLoop() override; | |
| void DoRun(Delegate* delegate) override; | |
| void Quit() override; | |
| private: | |
| void EnterExitRunLoop(CFRunLoopActivity activity) override; | |
| // True if Quit is called to stop the innermost MessagePump | |
| // (innermost_quittable_) but some other CFRunLoopRun loop (nesting_level_) | |
| // is running inside the MessagePump's innermost Run call. | |
| bool quit_pending_; | |
| DISALLOW_COPY_AND_ASSIGN(MessagePumpCFRunLoop); | |
| }; | |
| class BASE_EXPORT MessagePumpNSRunLoop : public MessagePumpCFRunLoopBase { | |
| public: | |
| MessagePumpNSRunLoop(); | |
| ~MessagePumpNSRunLoop() override; | |
| void DoRun(Delegate* delegate) override; | |
| void Quit() override; | |
| private: | |
| // A source that doesn't do anything but provide something signalable | |
| // attached to the run loop. This source will be signalled when Quit | |
| // is called, to cause the loop to wake up so that it can stop. | |
| CFRunLoopSourceRef quit_source_; | |
| // False after Quit is called. | |
| bool keep_running_; | |
| DISALLOW_COPY_AND_ASSIGN(MessagePumpNSRunLoop); | |
| }; | |
| #if defined(OS_IOS) | |
| // This is a fake message pump. It attaches sources to the main thread's | |
| // CFRunLoop, so PostTask() will work, but it is unable to drive the loop | |
| // directly, so calling Run() or Quit() are errors. | |
| class MessagePumpUIApplication : public MessagePumpCFRunLoopBase { | |
| public: | |
| MessagePumpUIApplication(); | |
| ~MessagePumpUIApplication() override; | |
| void DoRun(Delegate* delegate) override; | |
| void Quit() override; | |
| // This message pump can not spin the main message loop directly. Instead, | |
| // call |Attach()| to set up a delegate. It is an error to call |Run()|. | |
| virtual void Attach(Delegate* delegate); | |
| private: | |
| RunLoop* run_loop_; | |
| DISALLOW_COPY_AND_ASSIGN(MessagePumpUIApplication); | |
| }; | |
| #else | |
| // While in scope, permits posted tasks to be run in private AppKit run loop | |
| // modes that would otherwise make the UI unresponsive. E.g., menu fade out. | |
| class BASE_EXPORT ScopedPumpMessagesInPrivateModes { | |
| public: | |
| ScopedPumpMessagesInPrivateModes(); | |
| ~ScopedPumpMessagesInPrivateModes(); | |
| int GetModeMaskForTest(); | |
| private: | |
| DISALLOW_COPY_AND_ASSIGN(ScopedPumpMessagesInPrivateModes); | |
| }; | |
| class MessagePumpNSApplication : public MessagePumpCFRunLoopBase { | |
| public: | |
| MessagePumpNSApplication(); | |
| ~MessagePumpNSApplication() override; | |
| void DoRun(Delegate* delegate) override; | |
| void Quit() override; | |
| private: | |
| friend class ScopedPumpMessagesInPrivateModes; | |
| // False after Quit is called. | |
| bool keep_running_; | |
| // True if DoRun is managing its own run loop as opposed to letting | |
| // -[NSApplication run] handle it. The outermost run loop in the application | |
| // is managed by -[NSApplication run], inner run loops are handled by a loop | |
| // in DoRun. | |
| bool running_own_loop_; | |
| DISALLOW_COPY_AND_ASSIGN(MessagePumpNSApplication); | |
| }; | |
| class MessagePumpCrApplication : public MessagePumpNSApplication { | |
| public: | |
| MessagePumpCrApplication(); | |
| ~MessagePumpCrApplication() override; | |
| protected: | |
| // Returns nil if NSApp is currently in the middle of calling | |
| // -sendEvent. Requires NSApp implementing CrAppProtocol. | |
| AutoreleasePoolType* CreateAutoreleasePool() override; | |
| private: | |
| DISALLOW_COPY_AND_ASSIGN(MessagePumpCrApplication); | |
| }; | |
| #endif // !defined(OS_IOS) | |
| class BASE_EXPORT MessagePumpMac { | |
| public: | |
| // If not on the main thread, returns a new instance of | |
| // MessagePumpNSRunLoop. | |
| // | |
| // On the main thread, if NSApp exists and conforms to | |
| // CrAppProtocol, creates an instances of MessagePumpCrApplication. | |
| // | |
| // Otherwise creates an instance of MessagePumpNSApplication using a | |
| // default NSApplication. | |
| static std::unique_ptr<MessagePump> Create(); | |
| #if !defined(OS_IOS) | |
| // If a pump is created before the required CrAppProtocol is | |
| // created, the wrong MessagePump subclass could be used. | |
| // UsingCrApp() returns false if the message pump was created before | |
| // NSApp was initialized, or if NSApp does not implement | |
| // CrAppProtocol. NSApp must be initialized before calling. | |
| static bool UsingCrApp(); | |
| // Wrapper to query -[NSApp isHandlingSendEvent] from C++ code. | |
| // Requires NSApp to implement CrAppProtocol. | |
| static bool IsHandlingSendEvent(); | |
| #endif // !defined(OS_IOS) | |
| private: | |
| DISALLOW_IMPLICIT_CONSTRUCTORS(MessagePumpMac); | |
| }; | |
| // Tasks posted to the message loop are posted under this mode, as well | |
| // as kCFRunLoopCommonModes. | |
| extern const CFStringRef BASE_EXPORT kMessageLoopExclusiveRunLoopMode; | |
| } // namespace base | |
| #endif // BASE_MESSAGE_LOOP_MESSAGE_PUMP_MAC_H_ |