OLD | NEW |
| (Empty) |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #import "base/message_pump_mac.h" | |
6 | |
7 #import <Foundation/Foundation.h> | |
8 | |
9 #include <limits> | |
10 | |
11 #include "base/logging.h" | |
12 #include "base/run_loop.h" | |
13 #include "base/time.h" | |
14 | |
15 #if !defined(OS_IOS) | |
16 #import <AppKit/AppKit.h> | |
17 #endif // !defined(OS_IOS) | |
18 | |
19 namespace { | |
20 | |
21 void NoOp(void* info) { | |
22 } | |
23 | |
24 const CFTimeInterval kCFTimeIntervalMax = | |
25 std::numeric_limits<CFTimeInterval>::max(); | |
26 | |
27 #if !defined(OS_IOS) | |
28 // Set to true if MessagePumpMac::Create() is called before NSApp is | |
29 // initialized. Only accessed from the main thread. | |
30 bool g_not_using_cr_app = false; | |
31 #endif | |
32 | |
33 } // namespace | |
34 | |
35 namespace base { | |
36 | |
37 // A scoper for autorelease pools created from message pump run loops. | |
38 // Avoids dirtying up the ScopedNSAutoreleasePool interface for the rare | |
39 // case where an autorelease pool needs to be passed in. | |
40 class MessagePumpScopedAutoreleasePool { | |
41 public: | |
42 explicit MessagePumpScopedAutoreleasePool(MessagePumpCFRunLoopBase* pump) : | |
43 pool_(pump->CreateAutoreleasePool()) { | |
44 } | |
45 ~MessagePumpScopedAutoreleasePool() { | |
46 [pool_ drain]; | |
47 } | |
48 | |
49 private: | |
50 NSAutoreleasePool* pool_; | |
51 DISALLOW_COPY_AND_ASSIGN(MessagePumpScopedAutoreleasePool); | |
52 }; | |
53 | |
54 // Must be called on the run loop thread. | |
55 MessagePumpCFRunLoopBase::MessagePumpCFRunLoopBase() | |
56 : delegate_(NULL), | |
57 delayed_work_fire_time_(kCFTimeIntervalMax), | |
58 nesting_level_(0), | |
59 run_nesting_level_(0), | |
60 deepest_nesting_level_(0), | |
61 delegateless_work_(false), | |
62 delegateless_idle_work_(false) { | |
63 run_loop_ = CFRunLoopGetCurrent(); | |
64 CFRetain(run_loop_); | |
65 | |
66 // Set a repeating timer with a preposterous firing time and interval. The | |
67 // timer will effectively never fire as-is. The firing time will be adjusted | |
68 // as needed when ScheduleDelayedWork is called. | |
69 CFRunLoopTimerContext timer_context = CFRunLoopTimerContext(); | |
70 timer_context.info = this; | |
71 delayed_work_timer_ = CFRunLoopTimerCreate(NULL, // allocator | |
72 kCFTimeIntervalMax, // fire time | |
73 kCFTimeIntervalMax, // interval | |
74 0, // flags | |
75 0, // priority | |
76 RunDelayedWorkTimer, | |
77 &timer_context); | |
78 CFRunLoopAddTimer(run_loop_, delayed_work_timer_, kCFRunLoopCommonModes); | |
79 | |
80 CFRunLoopSourceContext source_context = CFRunLoopSourceContext(); | |
81 source_context.info = this; | |
82 source_context.perform = RunWorkSource; | |
83 work_source_ = CFRunLoopSourceCreate(NULL, // allocator | |
84 1, // priority | |
85 &source_context); | |
86 CFRunLoopAddSource(run_loop_, work_source_, kCFRunLoopCommonModes); | |
87 | |
88 source_context.perform = RunIdleWorkSource; | |
89 idle_work_source_ = CFRunLoopSourceCreate(NULL, // allocator | |
90 2, // priority | |
91 &source_context); | |
92 CFRunLoopAddSource(run_loop_, idle_work_source_, kCFRunLoopCommonModes); | |
93 | |
94 source_context.perform = RunNestingDeferredWorkSource; | |
95 nesting_deferred_work_source_ = CFRunLoopSourceCreate(NULL, // allocator | |
96 0, // priority | |
97 &source_context); | |
98 CFRunLoopAddSource(run_loop_, nesting_deferred_work_source_, | |
99 kCFRunLoopCommonModes); | |
100 | |
101 CFRunLoopObserverContext observer_context = CFRunLoopObserverContext(); | |
102 observer_context.info = this; | |
103 pre_wait_observer_ = CFRunLoopObserverCreate(NULL, // allocator | |
104 kCFRunLoopBeforeWaiting, | |
105 true, // repeat | |
106 0, // priority | |
107 PreWaitObserver, | |
108 &observer_context); | |
109 CFRunLoopAddObserver(run_loop_, pre_wait_observer_, kCFRunLoopCommonModes); | |
110 | |
111 pre_source_observer_ = CFRunLoopObserverCreate(NULL, // allocator | |
112 kCFRunLoopBeforeSources, | |
113 true, // repeat | |
114 0, // priority | |
115 PreSourceObserver, | |
116 &observer_context); | |
117 CFRunLoopAddObserver(run_loop_, pre_source_observer_, kCFRunLoopCommonModes); | |
118 | |
119 enter_exit_observer_ = CFRunLoopObserverCreate(NULL, // allocator | |
120 kCFRunLoopEntry | | |
121 kCFRunLoopExit, | |
122 true, // repeat | |
123 0, // priority | |
124 EnterExitObserver, | |
125 &observer_context); | |
126 CFRunLoopAddObserver(run_loop_, enter_exit_observer_, kCFRunLoopCommonModes); | |
127 } | |
128 | |
129 // Ideally called on the run loop thread. If other run loops were running | |
130 // lower on the run loop thread's stack when this object was created, the | |
131 // same number of run loops must be running when this object is destroyed. | |
132 MessagePumpCFRunLoopBase::~MessagePumpCFRunLoopBase() { | |
133 CFRunLoopRemoveObserver(run_loop_, enter_exit_observer_, | |
134 kCFRunLoopCommonModes); | |
135 CFRelease(enter_exit_observer_); | |
136 | |
137 CFRunLoopRemoveObserver(run_loop_, pre_source_observer_, | |
138 kCFRunLoopCommonModes); | |
139 CFRelease(pre_source_observer_); | |
140 | |
141 CFRunLoopRemoveObserver(run_loop_, pre_wait_observer_, | |
142 kCFRunLoopCommonModes); | |
143 CFRelease(pre_wait_observer_); | |
144 | |
145 CFRunLoopRemoveSource(run_loop_, nesting_deferred_work_source_, | |
146 kCFRunLoopCommonModes); | |
147 CFRelease(nesting_deferred_work_source_); | |
148 | |
149 CFRunLoopRemoveSource(run_loop_, idle_work_source_, kCFRunLoopCommonModes); | |
150 CFRelease(idle_work_source_); | |
151 | |
152 CFRunLoopRemoveSource(run_loop_, work_source_, kCFRunLoopCommonModes); | |
153 CFRelease(work_source_); | |
154 | |
155 CFRunLoopRemoveTimer(run_loop_, delayed_work_timer_, kCFRunLoopCommonModes); | |
156 CFRelease(delayed_work_timer_); | |
157 | |
158 CFRelease(run_loop_); | |
159 } | |
160 | |
161 // Must be called on the run loop thread. | |
162 void MessagePumpCFRunLoopBase::Run(Delegate* delegate) { | |
163 // nesting_level_ will be incremented in EnterExitRunLoop, so set | |
164 // run_nesting_level_ accordingly. | |
165 int last_run_nesting_level = run_nesting_level_; | |
166 run_nesting_level_ = nesting_level_ + 1; | |
167 | |
168 Delegate* last_delegate = delegate_; | |
169 SetDelegate(delegate); | |
170 | |
171 DoRun(delegate); | |
172 | |
173 // Restore the previous state of the object. | |
174 SetDelegate(last_delegate); | |
175 run_nesting_level_ = last_run_nesting_level; | |
176 } | |
177 | |
178 void MessagePumpCFRunLoopBase::SetDelegate(Delegate* delegate) { | |
179 delegate_ = delegate; | |
180 | |
181 if (delegate) { | |
182 // If any work showed up but could not be dispatched for want of a | |
183 // delegate, set it up for dispatch again now that a delegate is | |
184 // available. | |
185 if (delegateless_work_) { | |
186 CFRunLoopSourceSignal(work_source_); | |
187 delegateless_work_ = false; | |
188 } | |
189 if (delegateless_idle_work_) { | |
190 CFRunLoopSourceSignal(idle_work_source_); | |
191 delegateless_idle_work_ = false; | |
192 } | |
193 } | |
194 } | |
195 | |
196 // May be called on any thread. | |
197 void MessagePumpCFRunLoopBase::ScheduleWork() { | |
198 CFRunLoopSourceSignal(work_source_); | |
199 CFRunLoopWakeUp(run_loop_); | |
200 } | |
201 | |
202 // Must be called on the run loop thread. | |
203 void MessagePumpCFRunLoopBase::ScheduleDelayedWork( | |
204 const TimeTicks& delayed_work_time) { | |
205 TimeDelta delta = delayed_work_time - TimeTicks::Now(); | |
206 delayed_work_fire_time_ = CFAbsoluteTimeGetCurrent() + delta.InSecondsF(); | |
207 CFRunLoopTimerSetNextFireDate(delayed_work_timer_, delayed_work_fire_time_); | |
208 } | |
209 | |
210 // Called from the run loop. | |
211 // static | |
212 void MessagePumpCFRunLoopBase::RunDelayedWorkTimer(CFRunLoopTimerRef timer, | |
213 void* info) { | |
214 MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info); | |
215 | |
216 // The timer won't fire again until it's reset. | |
217 self->delayed_work_fire_time_ = kCFTimeIntervalMax; | |
218 | |
219 // CFRunLoopTimers fire outside of the priority scheme for CFRunLoopSources. | |
220 // In order to establish the proper priority in which work and delayed work | |
221 // are processed one for one, the timer used to schedule delayed work must | |
222 // signal a CFRunLoopSource used to dispatch both work and delayed work. | |
223 CFRunLoopSourceSignal(self->work_source_); | |
224 } | |
225 | |
226 // Called from the run loop. | |
227 // static | |
228 void MessagePumpCFRunLoopBase::RunWorkSource(void* info) { | |
229 MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info); | |
230 self->RunWork(); | |
231 } | |
232 | |
233 // Called by MessagePumpCFRunLoopBase::RunWorkSource. | |
234 bool MessagePumpCFRunLoopBase::RunWork() { | |
235 if (!delegate_) { | |
236 // This point can be reached with a NULL delegate_ if Run is not on the | |
237 // stack but foreign code is spinning the CFRunLoop. Arrange to come back | |
238 // here when a delegate is available. | |
239 delegateless_work_ = true; | |
240 return false; | |
241 } | |
242 | |
243 // The NSApplication-based run loop only drains the autorelease pool at each | |
244 // UI event (NSEvent). The autorelease pool is not drained for each | |
245 // CFRunLoopSource target that's run. Use a local pool for any autoreleased | |
246 // objects if the app is not currently handling a UI event to ensure they're | |
247 // released promptly even in the absence of UI events. | |
248 MessagePumpScopedAutoreleasePool autorelease_pool(this); | |
249 | |
250 // Call DoWork and DoDelayedWork once, and if something was done, arrange to | |
251 // come back here again as long as the loop is still running. | |
252 bool did_work = delegate_->DoWork(); | |
253 bool resignal_work_source = did_work; | |
254 | |
255 TimeTicks next_time; | |
256 delegate_->DoDelayedWork(&next_time); | |
257 if (!did_work) { | |
258 // Determine whether there's more delayed work, and if so, if it needs to | |
259 // be done at some point in the future or if it's already time to do it. | |
260 // Only do these checks if did_work is false. If did_work is true, this | |
261 // function, and therefore any additional delayed work, will get another | |
262 // chance to run before the loop goes to sleep. | |
263 bool more_delayed_work = !next_time.is_null(); | |
264 if (more_delayed_work) { | |
265 TimeDelta delay = next_time - TimeTicks::Now(); | |
266 if (delay > TimeDelta()) { | |
267 // There's more delayed work to be done in the future. | |
268 ScheduleDelayedWork(next_time); | |
269 } else { | |
270 // There's more delayed work to be done, and its time is in the past. | |
271 // Arrange to come back here directly as long as the loop is still | |
272 // running. | |
273 resignal_work_source = true; | |
274 } | |
275 } | |
276 } | |
277 | |
278 if (resignal_work_source) { | |
279 CFRunLoopSourceSignal(work_source_); | |
280 } | |
281 | |
282 return resignal_work_source; | |
283 } | |
284 | |
285 // Called from the run loop. | |
286 // static | |
287 void MessagePumpCFRunLoopBase::RunIdleWorkSource(void* info) { | |
288 MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info); | |
289 self->RunIdleWork(); | |
290 } | |
291 | |
292 // Called by MessagePumpCFRunLoopBase::RunIdleWorkSource. | |
293 bool MessagePumpCFRunLoopBase::RunIdleWork() { | |
294 if (!delegate_) { | |
295 // This point can be reached with a NULL delegate_ if Run is not on the | |
296 // stack but foreign code is spinning the CFRunLoop. Arrange to come back | |
297 // here when a delegate is available. | |
298 delegateless_idle_work_ = true; | |
299 return false; | |
300 } | |
301 | |
302 // The NSApplication-based run loop only drains the autorelease pool at each | |
303 // UI event (NSEvent). The autorelease pool is not drained for each | |
304 // CFRunLoopSource target that's run. Use a local pool for any autoreleased | |
305 // objects if the app is not currently handling a UI event to ensure they're | |
306 // released promptly even in the absence of UI events. | |
307 MessagePumpScopedAutoreleasePool autorelease_pool(this); | |
308 | |
309 // Call DoIdleWork once, and if something was done, arrange to come back here | |
310 // again as long as the loop is still running. | |
311 bool did_work = delegate_->DoIdleWork(); | |
312 if (did_work) { | |
313 CFRunLoopSourceSignal(idle_work_source_); | |
314 } | |
315 | |
316 return did_work; | |
317 } | |
318 | |
319 // Called from the run loop. | |
320 // static | |
321 void MessagePumpCFRunLoopBase::RunNestingDeferredWorkSource(void* info) { | |
322 MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info); | |
323 self->RunNestingDeferredWork(); | |
324 } | |
325 | |
326 // Called by MessagePumpCFRunLoopBase::RunNestingDeferredWorkSource. | |
327 bool MessagePumpCFRunLoopBase::RunNestingDeferredWork() { | |
328 if (!delegate_) { | |
329 // This point can be reached with a NULL delegate_ if Run is not on the | |
330 // stack but foreign code is spinning the CFRunLoop. There's no sense in | |
331 // attempting to do any work or signalling the work sources because | |
332 // without a delegate, work is not possible. | |
333 return false; | |
334 } | |
335 | |
336 // Immediately try work in priority order. | |
337 if (!RunWork()) { | |
338 if (!RunIdleWork()) { | |
339 return false; | |
340 } | |
341 } else { | |
342 // Work was done. Arrange for the loop to try non-nestable idle work on | |
343 // a subsequent pass. | |
344 CFRunLoopSourceSignal(idle_work_source_); | |
345 } | |
346 | |
347 return true; | |
348 } | |
349 | |
350 // Called before the run loop goes to sleep or exits, or processes sources. | |
351 void MessagePumpCFRunLoopBase::MaybeScheduleNestingDeferredWork() { | |
352 // deepest_nesting_level_ is set as run loops are entered. If the deepest | |
353 // level encountered is deeper than the current level, a nested loop | |
354 // (relative to the current level) ran since the last time nesting-deferred | |
355 // work was scheduled. When that situation is encountered, schedule | |
356 // nesting-deferred work in case any work was deferred because nested work | |
357 // was disallowed. | |
358 if (deepest_nesting_level_ > nesting_level_) { | |
359 deepest_nesting_level_ = nesting_level_; | |
360 CFRunLoopSourceSignal(nesting_deferred_work_source_); | |
361 } | |
362 } | |
363 | |
364 // Called from the run loop. | |
365 // static | |
366 void MessagePumpCFRunLoopBase::PreWaitObserver(CFRunLoopObserverRef observer, | |
367 CFRunLoopActivity activity, | |
368 void* info) { | |
369 MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info); | |
370 | |
371 // Attempt to do some idle work before going to sleep. | |
372 self->RunIdleWork(); | |
373 | |
374 // The run loop is about to go to sleep. If any of the work done since it | |
375 // started or woke up resulted in a nested run loop running, | |
376 // nesting-deferred work may have accumulated. Schedule it for processing | |
377 // if appropriate. | |
378 self->MaybeScheduleNestingDeferredWork(); | |
379 } | |
380 | |
381 // Called from the run loop. | |
382 // static | |
383 void MessagePumpCFRunLoopBase::PreSourceObserver(CFRunLoopObserverRef observer, | |
384 CFRunLoopActivity activity, | |
385 void* info) { | |
386 MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info); | |
387 | |
388 // The run loop has reached the top of the loop and is about to begin | |
389 // processing sources. If the last iteration of the loop at this nesting | |
390 // level did not sleep or exit, nesting-deferred work may have accumulated | |
391 // if a nested loop ran. Schedule nesting-deferred work for processing if | |
392 // appropriate. | |
393 self->MaybeScheduleNestingDeferredWork(); | |
394 } | |
395 | |
396 // Called from the run loop. | |
397 // static | |
398 void MessagePumpCFRunLoopBase::EnterExitObserver(CFRunLoopObserverRef observer, | |
399 CFRunLoopActivity activity, | |
400 void* info) { | |
401 MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info); | |
402 | |
403 switch (activity) { | |
404 case kCFRunLoopEntry: | |
405 ++self->nesting_level_; | |
406 if (self->nesting_level_ > self->deepest_nesting_level_) { | |
407 self->deepest_nesting_level_ = self->nesting_level_; | |
408 } | |
409 break; | |
410 | |
411 case kCFRunLoopExit: | |
412 // Not all run loops go to sleep. If a run loop is stopped before it | |
413 // goes to sleep due to a CFRunLoopStop call, or if the timeout passed | |
414 // to CFRunLoopRunInMode expires, the run loop may proceed directly from | |
415 // handling sources to exiting without any sleep. This most commonly | |
416 // occurs when CFRunLoopRunInMode is passed a timeout of 0, causing it | |
417 // to make a single pass through the loop and exit without sleep. Some | |
418 // native loops use CFRunLoop in this way. Because PreWaitObserver will | |
419 // not be called in these case, MaybeScheduleNestingDeferredWork needs | |
420 // to be called here, as the run loop exits. | |
421 // | |
422 // MaybeScheduleNestingDeferredWork consults self->nesting_level_ | |
423 // to determine whether to schedule nesting-deferred work. It expects | |
424 // the nesting level to be set to the depth of the loop that is going | |
425 // to sleep or exiting. It must be called before decrementing the | |
426 // value so that the value still corresponds to the level of the exiting | |
427 // loop. | |
428 self->MaybeScheduleNestingDeferredWork(); | |
429 --self->nesting_level_; | |
430 break; | |
431 | |
432 default: | |
433 break; | |
434 } | |
435 | |
436 self->EnterExitRunLoop(activity); | |
437 } | |
438 | |
439 // Called by MessagePumpCFRunLoopBase::EnterExitRunLoop. The default | |
440 // implementation is a no-op. | |
441 void MessagePumpCFRunLoopBase::EnterExitRunLoop(CFRunLoopActivity activity) { | |
442 } | |
443 | |
444 // Base version returns a standard NSAutoreleasePool. | |
445 NSAutoreleasePool* MessagePumpCFRunLoopBase::CreateAutoreleasePool() { | |
446 return [[NSAutoreleasePool alloc] init]; | |
447 } | |
448 | |
449 MessagePumpCFRunLoop::MessagePumpCFRunLoop() | |
450 : quit_pending_(false) { | |
451 } | |
452 | |
453 MessagePumpCFRunLoop::~MessagePumpCFRunLoop() {} | |
454 | |
455 // Called by MessagePumpCFRunLoopBase::DoRun. If other CFRunLoopRun loops were | |
456 // running lower on the run loop thread's stack when this object was created, | |
457 // the same number of CFRunLoopRun loops must be running for the outermost call | |
458 // to Run. Run/DoRun are reentrant after that point. | |
459 void MessagePumpCFRunLoop::DoRun(Delegate* delegate) { | |
460 // This is completely identical to calling CFRunLoopRun(), except autorelease | |
461 // pool management is introduced. | |
462 int result; | |
463 do { | |
464 MessagePumpScopedAutoreleasePool autorelease_pool(this); | |
465 result = CFRunLoopRunInMode(kCFRunLoopDefaultMode, | |
466 kCFTimeIntervalMax, | |
467 false); | |
468 } while (result != kCFRunLoopRunStopped && result != kCFRunLoopRunFinished); | |
469 } | |
470 | |
471 // Must be called on the run loop thread. | |
472 void MessagePumpCFRunLoop::Quit() { | |
473 // Stop the innermost run loop managed by this MessagePumpCFRunLoop object. | |
474 if (nesting_level() == run_nesting_level()) { | |
475 // This object is running the innermost loop, just stop it. | |
476 CFRunLoopStop(run_loop()); | |
477 } else { | |
478 // There's another loop running inside the loop managed by this object. | |
479 // In other words, someone else called CFRunLoopRunInMode on the same | |
480 // thread, deeper on the stack than the deepest Run call. Don't preempt | |
481 // other run loops, just mark this object to quit the innermost Run as | |
482 // soon as the other inner loops not managed by Run are done. | |
483 quit_pending_ = true; | |
484 } | |
485 } | |
486 | |
487 // Called by MessagePumpCFRunLoopBase::EnterExitObserver. | |
488 void MessagePumpCFRunLoop::EnterExitRunLoop(CFRunLoopActivity activity) { | |
489 if (activity == kCFRunLoopExit && | |
490 nesting_level() == run_nesting_level() && | |
491 quit_pending_) { | |
492 // Quit was called while loops other than those managed by this object | |
493 // were running further inside a run loop managed by this object. Now | |
494 // that all unmanaged inner run loops are gone, stop the loop running | |
495 // just inside Run. | |
496 CFRunLoopStop(run_loop()); | |
497 quit_pending_ = false; | |
498 } | |
499 } | |
500 | |
501 MessagePumpNSRunLoop::MessagePumpNSRunLoop() | |
502 : keep_running_(true) { | |
503 CFRunLoopSourceContext source_context = CFRunLoopSourceContext(); | |
504 source_context.perform = NoOp; | |
505 quit_source_ = CFRunLoopSourceCreate(NULL, // allocator | |
506 0, // priority | |
507 &source_context); | |
508 CFRunLoopAddSource(run_loop(), quit_source_, kCFRunLoopCommonModes); | |
509 } | |
510 | |
511 MessagePumpNSRunLoop::~MessagePumpNSRunLoop() { | |
512 CFRunLoopRemoveSource(run_loop(), quit_source_, kCFRunLoopCommonModes); | |
513 CFRelease(quit_source_); | |
514 } | |
515 | |
516 void MessagePumpNSRunLoop::DoRun(Delegate* delegate) { | |
517 while (keep_running_) { | |
518 // NSRunLoop manages autorelease pools itself. | |
519 [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode | |
520 beforeDate:[NSDate distantFuture]]; | |
521 } | |
522 | |
523 keep_running_ = true; | |
524 } | |
525 | |
526 void MessagePumpNSRunLoop::Quit() { | |
527 keep_running_ = false; | |
528 CFRunLoopSourceSignal(quit_source_); | |
529 CFRunLoopWakeUp(run_loop()); | |
530 } | |
531 | |
532 #if defined(OS_IOS) | |
533 MessagePumpUIApplication::MessagePumpUIApplication() | |
534 : run_loop_(NULL) { | |
535 } | |
536 | |
537 MessagePumpUIApplication::~MessagePumpUIApplication() {} | |
538 | |
539 void MessagePumpUIApplication::DoRun(Delegate* delegate) { | |
540 NOTREACHED(); | |
541 } | |
542 | |
543 void MessagePumpUIApplication::Quit() { | |
544 NOTREACHED(); | |
545 } | |
546 | |
547 void MessagePumpUIApplication::Attach(Delegate* delegate) { | |
548 DCHECK(!run_loop_); | |
549 run_loop_ = new base::RunLoop(); | |
550 CHECK(run_loop_->BeforeRun()); | |
551 SetDelegate(delegate); | |
552 } | |
553 | |
554 #else | |
555 | |
556 MessagePumpNSApplication::MessagePumpNSApplication() | |
557 : keep_running_(true), | |
558 running_own_loop_(false) { | |
559 } | |
560 | |
561 MessagePumpNSApplication::~MessagePumpNSApplication() {} | |
562 | |
563 void MessagePumpNSApplication::DoRun(Delegate* delegate) { | |
564 bool last_running_own_loop_ = running_own_loop_; | |
565 | |
566 // NSApp must be initialized by calling: | |
567 // [{some class which implements CrAppProtocol} sharedApplication] | |
568 // Most likely candidates are CrApplication or BrowserCrApplication. | |
569 // These can be initialized from C++ code by calling | |
570 // RegisterCrApp() or RegisterBrowserCrApp(). | |
571 CHECK(NSApp); | |
572 | |
573 if (![NSApp isRunning]) { | |
574 running_own_loop_ = false; | |
575 // NSApplication manages autorelease pools itself when run this way. | |
576 [NSApp run]; | |
577 } else { | |
578 running_own_loop_ = true; | |
579 NSDate* distant_future = [NSDate distantFuture]; | |
580 while (keep_running_) { | |
581 MessagePumpScopedAutoreleasePool autorelease_pool(this); | |
582 NSEvent* event = [NSApp nextEventMatchingMask:NSAnyEventMask | |
583 untilDate:distant_future | |
584 inMode:NSDefaultRunLoopMode | |
585 dequeue:YES]; | |
586 if (event) { | |
587 [NSApp sendEvent:event]; | |
588 } | |
589 } | |
590 keep_running_ = true; | |
591 } | |
592 | |
593 running_own_loop_ = last_running_own_loop_; | |
594 } | |
595 | |
596 void MessagePumpNSApplication::Quit() { | |
597 if (!running_own_loop_) { | |
598 [[NSApplication sharedApplication] stop:nil]; | |
599 } else { | |
600 keep_running_ = false; | |
601 } | |
602 | |
603 // Send a fake event to wake the loop up. | |
604 [NSApp postEvent:[NSEvent otherEventWithType:NSApplicationDefined | |
605 location:NSMakePoint(0, 0) | |
606 modifierFlags:0 | |
607 timestamp:0 | |
608 windowNumber:0 | |
609 context:NULL | |
610 subtype:0 | |
611 data1:0 | |
612 data2:0] | |
613 atStart:NO]; | |
614 } | |
615 | |
616 MessagePumpCrApplication::MessagePumpCrApplication() { | |
617 } | |
618 | |
619 // Prevents an autorelease pool from being created if the app is in the midst of | |
620 // handling a UI event because various parts of AppKit depend on objects that | |
621 // are created while handling a UI event to be autoreleased in the event loop. | |
622 // An example of this is NSWindowController. When a window with a window | |
623 // controller is closed it goes through a stack like this: | |
624 // (Several stack frames elided for clarity) | |
625 // | |
626 // #0 [NSWindowController autorelease] | |
627 // #1 DoAClose | |
628 // #2 MessagePumpCFRunLoopBase::DoWork() | |
629 // #3 [NSRunLoop run] | |
630 // #4 [NSButton performClick:] | |
631 // #5 [NSWindow sendEvent:] | |
632 // #6 [NSApp sendEvent:] | |
633 // #7 [NSApp run] | |
634 // | |
635 // -performClick: spins a nested run loop. If the pool created in DoWork was a | |
636 // standard NSAutoreleasePool, it would release the objects that were | |
637 // autoreleased into it once DoWork released it. This would cause the window | |
638 // controller, which autoreleased itself in frame #0, to release itself, and | |
639 // possibly free itself. Unfortunately this window controller controls the | |
640 // window in frame #5. When the stack is unwound to frame #5, the window would | |
641 // no longer exists and crashes may occur. Apple gets around this by never | |
642 // releasing the pool it creates in frame #4, and letting frame #7 clean it up | |
643 // when it cleans up the pool that wraps frame #7. When an autorelease pool is | |
644 // released it releases all other pools that were created after it on the | |
645 // autorelease pool stack. | |
646 // | |
647 // CrApplication is responsible for setting handlingSendEvent to true just | |
648 // before it sends the event through the event handling mechanism, and | |
649 // returning it to its previous value once the event has been sent. | |
650 NSAutoreleasePool* MessagePumpCrApplication::CreateAutoreleasePool() { | |
651 if (MessagePumpMac::IsHandlingSendEvent()) | |
652 return nil; | |
653 return MessagePumpNSApplication::CreateAutoreleasePool(); | |
654 } | |
655 | |
656 // static | |
657 bool MessagePumpMac::UsingCrApp() { | |
658 DCHECK([NSThread isMainThread]); | |
659 | |
660 // If NSApp is still not initialized, then the subclass used cannot | |
661 // be determined. | |
662 DCHECK(NSApp); | |
663 | |
664 // The pump was created using MessagePumpNSApplication. | |
665 if (g_not_using_cr_app) | |
666 return false; | |
667 | |
668 return [NSApp conformsToProtocol:@protocol(CrAppProtocol)]; | |
669 } | |
670 | |
671 // static | |
672 bool MessagePumpMac::IsHandlingSendEvent() { | |
673 DCHECK([NSApp conformsToProtocol:@protocol(CrAppProtocol)]); | |
674 NSObject<CrAppProtocol>* app = static_cast<NSObject<CrAppProtocol>*>(NSApp); | |
675 return [app isHandlingSendEvent]; | |
676 } | |
677 #endif // !defined(OS_IOS) | |
678 | |
679 // static | |
680 MessagePump* MessagePumpMac::Create() { | |
681 if ([NSThread isMainThread]) { | |
682 #if defined(OS_IOS) | |
683 return new MessagePumpUIApplication; | |
684 #else | |
685 if ([NSApp conformsToProtocol:@protocol(CrAppProtocol)]) | |
686 return new MessagePumpCrApplication; | |
687 | |
688 // The main-thread MessagePump implementations REQUIRE an NSApp. | |
689 // Executables which have specific requirements for their | |
690 // NSApplication subclass should initialize appropriately before | |
691 // creating an event loop. | |
692 [NSApplication sharedApplication]; | |
693 g_not_using_cr_app = true; | |
694 return new MessagePumpNSApplication; | |
695 #endif | |
696 } | |
697 | |
698 return new MessagePumpNSRunLoop; | |
699 } | |
700 | |
701 } // namespace base | |
OLD | NEW |