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