• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2012 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 // The basis for all native run loops on the Mac is the CFRunLoop.  It can be
6 // used directly, it can be used as the driving force behind the similar
7 // Foundation NSRunLoop, and it can be used to implement higher-level event
8 // loops such as the NSApplication event loop.
9 //
10 // This file introduces a basic CFRunLoop-based implementation of the
11 // MessagePump interface called CFRunLoopBase.  CFRunLoopBase contains all
12 // of the machinery necessary to dispatch events to a delegate, but does not
13 // implement the specific run loop.  Concrete subclasses must provide their
14 // own DoRun and DoQuit implementations.
15 //
16 // A concrete subclass that just runs a CFRunLoop loop is provided in
17 // MessagePumpCFRunLoop.  For an NSRunLoop, the similar MessagePumpNSRunLoop
18 // is provided.
19 //
20 // For the application's event loop, an implementation based on AppKit's
21 // NSApplication event system is provided in MessagePumpNSApplication.
22 //
23 // Typically, MessagePumpNSApplication only makes sense on a Cocoa
24 // application's main thread.  If a CFRunLoop-based message pump is needed on
25 // any other thread, one of the other concrete subclasses is preferable.
26 // MessagePumpMac::Create is defined, which returns a new NSApplication-based
27 // or NSRunLoop-based MessagePump subclass depending on which thread it is
28 // called on.
29 
30 #ifndef BASE_MESSAGE_LOOP_MESSAGE_PUMP_MAC_H_
31 #define BASE_MESSAGE_LOOP_MESSAGE_PUMP_MAC_H_
32 
33 #include "base/memory/raw_ptr.h"
34 #include "base/message_loop/message_pump.h"
35 
36 
37 #include <CoreFoundation/CoreFoundation.h>
38 #include <memory>
39 
40 #include "base/containers/stack.h"
41 #include "base/message_loop/timer_slack.h"
42 #include "build/build_config.h"
43 #include "third_party/abseil-cpp/absl/types/optional.h"
44 
45 #if defined(__OBJC__)
46 #if BUILDFLAG(IS_IOS)
47 #import <Foundation/Foundation.h>
48 #else
49 #import <AppKit/AppKit.h>
50 
51 // Clients must subclass NSApplication and implement this protocol if they use
52 // MessagePumpMac.
53 @protocol CrAppProtocol
54 // Must return true if -[NSApplication sendEvent:] is currently on the stack.
55 // See the comment for |CreateAutoreleasePool()| in the cc file for why this is
56 // necessary.
57 - (BOOL)isHandlingSendEvent;
58 @end
59 #endif  // BUILDFLAG(IS_IOS)
60 #endif  // defined(__OBJC__)
61 
62 namespace base {
63 
64 class RunLoop;
65 
66 // AutoreleasePoolType is a proxy type for autorelease pools. Its definition
67 // depends on the translation unit (TU) in which this header appears. In pure
68 // C++ TUs, it is defined as a forward C++ class declaration (that is never
69 // defined), because autorelease pools are an Objective-C concept. In Automatic
70 // Reference Counting (ARC) Objective-C TUs, it is similarly defined as a
71 // forward C++ class declaration, because clang will not allow the type
72 // "NSAutoreleasePool" in such TUs. Finally, in Manual Retain Release (MRR)
73 // Objective-C TUs, it is a type alias for NSAutoreleasePool. In all cases, a
74 // method that takes or returns an NSAutoreleasePool* can use
75 // AutoreleasePoolType* instead.
76 #if !defined(__OBJC__) || __has_feature(objc_arc)
77 class AutoreleasePoolType;
78 #else   // !defined(__OBJC__) || __has_feature(objc_arc)
79 typedef NSAutoreleasePool AutoreleasePoolType;
80 #endif  // !defined(__OBJC__) || __has_feature(objc_arc)
81 
82 class BASE_EXPORT MessagePumpCFRunLoopBase : public MessagePump {
83  public:
84   MessagePumpCFRunLoopBase(const MessagePumpCFRunLoopBase&) = delete;
85   MessagePumpCFRunLoopBase& operator=(const MessagePumpCFRunLoopBase&) = delete;
86 
87   static void InitializeFeatures();
88 
89   // MessagePump:
90   void Run(Delegate* delegate) override;
91   void Quit() override;
92   void ScheduleWork() override;
93   void ScheduleDelayedWork(
94       const Delegate::NextWorkInfo& next_work_info) override;
95   void SetTimerSlack(TimerSlack timer_slack) override;
96 
97 #if BUILDFLAG(IS_IOS)
98   // Some iOS message pumps do not support calling |Run()| to spin the main
99   // message loop directly.  Instead, call |Attach()| to set up a delegate, then
100   // |Detach()| before destroying the message pump.  These methods do nothing if
101   // the message pump supports calling |Run()| and |Quit()|.
102   virtual void Attach(Delegate* delegate);
103   virtual void Detach();
104 #endif  // BUILDFLAG(IS_IOS)
105 
106  protected:
107   // Needs access to CreateAutoreleasePool.
108   friend class MessagePumpScopedAutoreleasePool;
109   friend class TestMessagePumpCFRunLoopBase;
110 
111   // Tasks will be pumped in the run loop modes described by
112   // |initial_mode_mask|, which maps bits to the index of an internal array of
113   // run loop mode identifiers.
114   explicit MessagePumpCFRunLoopBase(int initial_mode_mask);
115   ~MessagePumpCFRunLoopBase() override;
116 
117   // Subclasses should implement the work they need to do in MessagePump::Run
118   // in the DoRun method.  MessagePumpCFRunLoopBase::Run calls DoRun directly.
119   // This arrangement is used because MessagePumpCFRunLoopBase needs to set
120   // up and tear down things before and after the "meat" of DoRun.
121   virtual void DoRun(Delegate* delegate) = 0;
122 
123   // Similar to DoRun, this allows subclasses to perform custom handling when
124   // quitting a run loop. Return true if the quit took effect immediately;
125   // otherwise call OnDidQuit() when the quit is actually applied (e.g., a
126   // nested native runloop exited).
127   virtual bool DoQuit() = 0;
128 
129   // Should be called by subclasses to signal when a deferred quit takes place.
130   void OnDidQuit();
131 
132   // Accessors for private data members to be used by subclasses.
run_loop()133   CFRunLoopRef run_loop() const { return run_loop_; }
nesting_level()134   int nesting_level() const { return nesting_level_; }
run_nesting_level()135   int run_nesting_level() const { return run_nesting_level_; }
keep_running()136   bool keep_running() const { return keep_running_; }
137 
138 #if BUILDFLAG(IS_IOS)
139   void OnAttach();
140   void OnDetach();
141 #endif
142 
143   // Sets this pump's delegate.  Signals the appropriate sources if
144   // |delegateless_work_| is true.  |delegate| can be NULL.
145   void SetDelegate(Delegate* delegate);
146 
147   // Return an autorelease pool to wrap around any work being performed.
148   // In some cases, CreateAutoreleasePool may return nil intentionally to
149   // preventing an autorelease pool from being created, allowing any
150   // objects autoreleased by work to fall into the current autorelease pool.
151   virtual AutoreleasePoolType* CreateAutoreleasePool();
152 
153   // Enable and disable entries in |enabled_modes_| to match |mode_mask|.
154   void SetModeMask(int mode_mask);
155 
156   // Get the current mode mask from |enabled_modes_|.
157   int GetModeMask() const;
158 
159  private:
160   class ScopedModeEnabler;
161 
162   // The maximum number of run loop modes that can be monitored.
163   static constexpr int kNumModes = 4;
164 
165   // Timer callback scheduled by ScheduleDelayedWork.  This does not do any
166   // work, but it signals |work_source_| so that delayed work can be performed
167   // within the appropriate priority constraints.
168   static void RunDelayedWorkTimer(CFRunLoopTimerRef timer, void* info);
169 
170   // Perform highest-priority work.  This is associated with |work_source_|
171   // signalled by ScheduleWork or RunDelayedWorkTimer.  The static method calls
172   // the instance method; the instance method returns true if it resignalled
173   // |work_source_| to be called again from the loop.
174   static void RunWorkSource(void* info);
175   bool RunWork();
176 
177   // Perform idle-priority work.  This is normally called by PreWaitObserver,
178   // but can also be invoked from RunNestingDeferredWork when returning from a
179   // nested loop.  When this function actually does perform idle work, it will
180   // re-signal the |work_source_|.
181   void RunIdleWork();
182 
183   // Perform work that may have been deferred because it was not runnable
184   // within a nested run loop.  This is associated with
185   // |nesting_deferred_work_source_| and is signalled by
186   // MaybeScheduleNestingDeferredWork when returning from a nested loop,
187   // so that an outer loop will be able to perform the necessary tasks if it
188   // permits nestable tasks.
189   static void RunNestingDeferredWorkSource(void* info);
190   void RunNestingDeferredWork();
191 
192   // Called before the run loop goes to sleep to notify delegate.
193   void BeforeWait();
194 
195   // Schedules possible nesting-deferred work to be processed before the run
196   // loop goes to sleep, exits, or begins processing sources at the top of its
197   // loop.  If this function detects that a nested loop had run since the
198   // previous attempt to schedule nesting-deferred work, it will schedule a
199   // call to RunNestingDeferredWorkSource.
200   void MaybeScheduleNestingDeferredWork();
201 
202   // Observer callback responsible for performing idle-priority work, before
203   // the run loop goes to sleep.  Associated with |pre_wait_observer_|.
204   static void PreWaitObserver(CFRunLoopObserverRef observer,
205                               CFRunLoopActivity activity, void* info);
206 
207   static void AfterWaitObserver(CFRunLoopObserverRef observer,
208                                 CFRunLoopActivity activity,
209                                 void* info);
210 
211   // Observer callback called before the run loop processes any sources.
212   // Associated with |pre_source_observer_|.
213   static void PreSourceObserver(CFRunLoopObserverRef observer,
214                                 CFRunLoopActivity activity, void* info);
215 
216   // Observer callback called when the run loop starts and stops, at the
217   // beginning and end of calls to CFRunLoopRun.  This is used to maintain
218   // |nesting_level_|.  Associated with |enter_exit_observer_|.
219   static void EnterExitObserver(CFRunLoopObserverRef observer,
220                                 CFRunLoopActivity activity, void* info);
221 
222   // Called by EnterExitObserver after performing maintenance on
223   // |nesting_level_|. This allows subclasses an opportunity to perform
224   // additional processing on the basis of run loops starting and stopping.
225   virtual void EnterExitRunLoop(CFRunLoopActivity activity);
226 
227   // Gets rid of the top work item scope.
228   void PopWorkItemScope();
229 
230   // Starts tracking a new work item.
231   void PushWorkItemScope();
232 
233   // The thread's run loop.
234   CFRunLoopRef run_loop_;
235 
236   // The enabled modes. Posted tasks may run in any non-null entry.
237   std::unique_ptr<ScopedModeEnabler> enabled_modes_[kNumModes];
238 
239   // The timer, sources, and observers are described above alongside their
240   // callbacks.
241   CFRunLoopTimerRef delayed_work_timer_;
242   CFRunLoopSourceRef work_source_;
243   CFRunLoopSourceRef nesting_deferred_work_source_;
244   CFRunLoopObserverRef pre_wait_observer_;
245   CFRunLoopObserverRef after_wait_observer_;
246   CFRunLoopObserverRef pre_source_observer_;
247   CFRunLoopObserverRef enter_exit_observer_;
248 
249   // (weak) Delegate passed as an argument to the innermost Run call.
250   raw_ptr<Delegate> delegate_;
251 
252   base::TimerSlack timer_slack_;
253 
254   // Time at which `delayed_work_timer_` is set to fire.
255   base::TimeTicks delayed_work_scheduled_at_ = base::TimeTicks::Max();
256 
257   // The recursion depth of the currently-executing CFRunLoopRun loop on the
258   // run loop's thread.  0 if no run loops are running inside of whatever scope
259   // the object was created in.
260   int nesting_level_;
261 
262   // The recursion depth (calculated in the same way as |nesting_level_|) of the
263   // innermost executing CFRunLoopRun loop started by a call to Run.
264   int run_nesting_level_;
265 
266   // The deepest (numerically highest) recursion depth encountered since the
267   // most recent attempt to run nesting-deferred work.
268   int deepest_nesting_level_;
269 
270   // Whether we should continue running application tasks. Set to false when
271   // Quit() is called for the innermost run loop.
272   bool keep_running_;
273 
274   // "Delegateless" work flags are set when work is ready to be performed but
275   // must wait until a delegate is available to process it.  This can happen
276   // when a MessagePumpCFRunLoopBase is instantiated and work arrives without
277   // any call to Run on the stack.  The Run method will check for delegateless
278   // work on entry and redispatch it as needed once a delegate is available.
279   bool delegateless_work_;
280 
281   // Used to keep track of the native event work items processed by the message
282   // pump. Made of optionals because tracking can be suspended when it's
283   // determined the loop is not processing a native event but the depth of the
284   // stack should match |nesting_level_| at all times. A nullopt is also used
285   // as a stand-in during delegateless operation.
286   base::stack<absl::optional<base::MessagePump::Delegate::ScopedDoWorkItem>>
287       stack_;
288 };
289 
290 class BASE_EXPORT MessagePumpCFRunLoop : public MessagePumpCFRunLoopBase {
291  public:
292   MessagePumpCFRunLoop();
293 
294   MessagePumpCFRunLoop(const MessagePumpCFRunLoop&) = delete;
295   MessagePumpCFRunLoop& operator=(const MessagePumpCFRunLoop&) = delete;
296 
297   ~MessagePumpCFRunLoop() override;
298 
299   void DoRun(Delegate* delegate) override;
300   bool DoQuit() override;
301 
302  private:
303   void EnterExitRunLoop(CFRunLoopActivity activity) override;
304 
305   // True if Quit is called to stop the innermost MessagePump
306   // (|innermost_quittable_|) but some other CFRunLoopRun loop
307   // (|nesting_level_|) is running inside the MessagePump's innermost Run call.
308   bool quit_pending_;
309 };
310 
311 class BASE_EXPORT MessagePumpNSRunLoop : public MessagePumpCFRunLoopBase {
312  public:
313   MessagePumpNSRunLoop();
314 
315   MessagePumpNSRunLoop(const MessagePumpNSRunLoop&) = delete;
316   MessagePumpNSRunLoop& operator=(const MessagePumpNSRunLoop&) = delete;
317 
318   ~MessagePumpNSRunLoop() override;
319 
320   void DoRun(Delegate* delegate) override;
321   bool DoQuit() override;
322 
323  private:
324   // A source that doesn't do anything but provide something signalable
325   // attached to the run loop.  This source will be signalled when Quit
326   // is called, to cause the loop to wake up so that it can stop.
327   CFRunLoopSourceRef quit_source_;
328 };
329 
330 #if BUILDFLAG(IS_IOS)
331 // This is a fake message pump.  It attaches sources to the main thread's
332 // CFRunLoop, so PostTask() will work, but it is unable to drive the loop
333 // directly, so calling Run() or Quit() are errors.
334 class MessagePumpUIApplication : public MessagePumpCFRunLoopBase {
335  public:
336   MessagePumpUIApplication();
337 
338   MessagePumpUIApplication(const MessagePumpUIApplication&) = delete;
339   MessagePumpUIApplication& operator=(const MessagePumpUIApplication&) = delete;
340 
341   ~MessagePumpUIApplication() override;
342   void DoRun(Delegate* delegate) override;
343   bool DoQuit() override;
344 
345   // MessagePumpCFRunLoopBase.
346   // MessagePumpUIApplication can not spin the main message loop directly.
347   // Instead, call |Attach()| to set up a delegate.  It is an error to call
348   // |Run()|.
349   void Attach(Delegate* delegate) override;
350   void Detach() override;
351 
352  private:
353   RunLoop* run_loop_;
354 };
355 
356 #else
357 
358 // While in scope, permits posted tasks to be run in private AppKit run loop
359 // modes that would otherwise make the UI unresponsive. E.g., menu fade out.
360 class BASE_EXPORT ScopedPumpMessagesInPrivateModes {
361  public:
362   ScopedPumpMessagesInPrivateModes();
363 
364   ScopedPumpMessagesInPrivateModes(const ScopedPumpMessagesInPrivateModes&) =
365       delete;
366   ScopedPumpMessagesInPrivateModes& operator=(
367       const ScopedPumpMessagesInPrivateModes&) = delete;
368 
369   ~ScopedPumpMessagesInPrivateModes();
370 
371   int GetModeMaskForTest();
372 };
373 
374 class MessagePumpNSApplication : public MessagePumpCFRunLoopBase {
375  public:
376   MessagePumpNSApplication();
377 
378   MessagePumpNSApplication(const MessagePumpNSApplication&) = delete;
379   MessagePumpNSApplication& operator=(const MessagePumpNSApplication&) = delete;
380 
381   ~MessagePumpNSApplication() override;
382 
383   void DoRun(Delegate* delegate) override;
384   bool DoQuit() override;
385 
386  private:
387   friend class ScopedPumpMessagesInPrivateModes;
388 
389   void EnterExitRunLoop(CFRunLoopActivity activity) override;
390 
391   // True if DoRun is managing its own run loop as opposed to letting
392   // -[NSApplication run] handle it.  The outermost run loop in the application
393   // is managed by -[NSApplication run], inner run loops are handled by a loop
394   // in DoRun.
395   bool running_own_loop_;
396 
397   // True if Quit() was called while a modal window was shown and needed to be
398   // deferred.
399   bool quit_pending_;
400 };
401 
402 class MessagePumpCrApplication : public MessagePumpNSApplication {
403  public:
404   MessagePumpCrApplication();
405 
406   MessagePumpCrApplication(const MessagePumpCrApplication&) = delete;
407   MessagePumpCrApplication& operator=(const MessagePumpCrApplication&) = delete;
408 
409   ~MessagePumpCrApplication() override;
410 
411  protected:
412   // Returns nil if NSApp is currently in the middle of calling
413   // -sendEvent.  Requires NSApp implementing CrAppProtocol.
414   AutoreleasePoolType* CreateAutoreleasePool() override;
415 };
416 #endif  // BUILDFLAG(IS_IOS)
417 
418 class BASE_EXPORT MessagePumpMac {
419  public:
420   MessagePumpMac() = delete;
421   MessagePumpMac(const MessagePumpMac&) = delete;
422   MessagePumpMac& operator=(const MessagePumpMac&) = delete;
423 
424   // If not on the main thread, returns a new instance of
425   // MessagePumpNSRunLoop.
426   //
427   // On the main thread, if NSApp exists and conforms to
428   // CrAppProtocol, creates an instances of MessagePumpCrApplication.
429   //
430   // Otherwise creates an instance of MessagePumpNSApplication using a
431   // default NSApplication.
432   static std::unique_ptr<MessagePump> Create();
433 
434 #if !BUILDFLAG(IS_IOS)
435   // If a pump is created before the required CrAppProtocol is
436   // created, the wrong MessagePump subclass could be used.
437   // UsingCrApp() returns false if the message pump was created before
438   // NSApp was initialized, or if NSApp does not implement
439   // CrAppProtocol.  NSApp must be initialized before calling.
440   static bool UsingCrApp();
441 
442   // Wrapper to query -[NSApp isHandlingSendEvent] from C++ code.
443   // Requires NSApp to implement CrAppProtocol.
444   static bool IsHandlingSendEvent();
445 #endif  // !BUILDFLAG(IS_IOS)
446 };
447 
448 // Tasks posted to the message loop are posted under this mode, as well
449 // as kCFRunLoopCommonModes.
450 extern const CFStringRef BASE_EXPORT kMessageLoopExclusiveRunLoopMode;
451 
452 }  // namespace base
453 
454 #endif  // BASE_MESSAGE_LOOP_MESSAGE_PUMP_MAC_H_
455