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