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