Bumping gaia.json for 2 gaia revision(s) a=gaia-bump
[gecko.git] / widget / cocoa / nsAppShell.mm
blob4b0de7e401dddf839c3ab82e7913b79cf96d00c4
1 /* -*- Mode: c++; tab-width: 2; indent-tabs-mode: nil; -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3  * License, v. 2.0. If a copy of the MPL was not distributed with this
4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 /*
7  * Runs the main native Cocoa run loop, interrupting it as needed to process
8  * Gecko events.
9  */
11 #import <Cocoa/Cocoa.h>
13 #include "CustomCocoaEvents.h"
14 #include "mozilla/WidgetTraceEvent.h"
15 #include "nsAppShell.h"
16 #include "nsCOMPtr.h"
17 #include "nsIFile.h"
18 #include "nsDirectoryServiceDefs.h"
19 #include "nsString.h"
20 #include "nsIRollupListener.h"
21 #include "nsIWidget.h"
22 #include "nsThreadUtils.h"
23 #include "nsIWindowMediator.h"
24 #include "nsServiceManagerUtils.h"
25 #include "nsIInterfaceRequestor.h"
26 #include "nsIWebBrowserChrome.h"
27 #include "nsObjCExceptions.h"
28 #include "nsCocoaFeatures.h"
29 #include "nsCocoaUtils.h"
30 #include "nsChildView.h"
31 #include "nsToolkit.h"
32 #include "TextInputHandler.h"
33 #include "mozilla/HangMonitor.h"
34 #include "GeckoProfiler.h"
35 #include "pratom.h"
37 #include <IOKit/pwr_mgt/IOPMLib.h>
38 #include "nsIDOMWakeLockListener.h"
39 #include "nsIPowerManagerService.h"
41 using namespace mozilla::widget;
43 // A wake lock listener that disables screen saver when requested by
44 // Gecko. For example when we're playing video in a foreground tab we
45 // don't want the screen saver to turn on.
47 class MacWakeLockListener MOZ_FINAL : public nsIDOMMozWakeLockListener {
48 public:
49   NS_DECL_ISUPPORTS;
51 private:
52   ~MacWakeLockListener() {}
54   IOPMAssertionID mAssertionID = kIOPMNullAssertionID;
56   NS_IMETHOD Callback(const nsAString& aTopic, const nsAString& aState) {
57     if (!aTopic.EqualsASCII("screen")) {
58       return NS_OK;
59     }
60     // Note the wake lock code ensures that we're not sent duplicate
61     // "locked-foreground" notifications when multiple wake locks are held.
62     if (aState.EqualsASCII("locked-foreground")) {
63       // Prevent screen saver.
64       CFStringRef cf_topic =
65         ::CFStringCreateWithCharacters(kCFAllocatorDefault,
66                                        reinterpret_cast<const UniChar*>
67                                          (aTopic.Data()),
68                                        aTopic.Length());
69       IOReturn success =
70         ::IOPMAssertionCreateWithName(kIOPMAssertionTypeNoDisplaySleep,
71                                       kIOPMAssertionLevelOn,
72                                       cf_topic,
73                                       &mAssertionID);
74       CFRelease(cf_topic);
75       if (success != kIOReturnSuccess) {
76         NS_WARNING("failed to disable screensaver");
77       }
78     } else {
79       // Re-enable screen saver.
80       NS_WARNING("Releasing screensaver");
81       if (mAssertionID != kIOPMNullAssertionID) {
82         IOReturn result = ::IOPMAssertionRelease(mAssertionID);
83         if (result != kIOReturnSuccess) {
84           NS_WARNING("failed to release screensaver");
85         }
86       }
87     }
88     return NS_OK;
89   }
92 // defined in nsCocoaWindow.mm
93 extern int32_t             gXULModalLevel;
95 static bool gAppShellMethodsSwizzled = false;
97 @implementation GeckoNSApplication
99 - (void)sendEvent:(NSEvent *)anEvent
101   mozilla::HangMonitor::NotifyActivity();
102   if ([anEvent type] == NSApplicationDefined &&
103       [anEvent subtype] == kEventSubtypeTrace) {
104     mozilla::SignalTracerThread();
105     return;
106   }
107   [super sendEvent:anEvent];
110 - (NSEvent*)nextEventMatchingMask:(NSUInteger)mask
111                         untilDate:(NSDate*)expiration
112                            inMode:(NSString*)mode
113                           dequeue:(BOOL)flag
115   if (expiration) {
116     mozilla::HangMonitor::Suspend();
117   }
118   return [super nextEventMatchingMask:mask
119           untilDate:expiration inMode:mode dequeue:flag];
122 @end
125 // AppShellDelegate
127 // Cocoa bridge class.  An object of this class is registered to receive
128 // notifications.
130 @interface AppShellDelegate : NSObject
132   @private
133     nsAppShell* mAppShell;
136 - (id)initWithAppShell:(nsAppShell*)aAppShell;
137 - (void)applicationWillTerminate:(NSNotification*)aNotification;
138 - (void)beginMenuTracking:(NSNotification*)aNotification;
139 @end
141 // nsAppShell implementation
143 NS_IMETHODIMP
144 nsAppShell::ResumeNative(void)
146   nsresult retval = nsBaseAppShell::ResumeNative();
147   if (NS_SUCCEEDED(retval) && (mSuspendNativeCount == 0) &&
148       mSkippedNativeCallback)
149   {
150     mSkippedNativeCallback = false;
151     ScheduleNativeEventCallback();
152   }
153   return retval;
156 nsAppShell::nsAppShell()
157 : mAutoreleasePools(nullptr)
158 , mDelegate(nullptr)
159 , mCFRunLoop(NULL)
160 , mCFRunLoopSource(NULL)
161 , mRunningEventLoop(false)
162 , mStarted(false)
163 , mTerminated(false)
164 , mSkippedNativeCallback(false)
165 , mNativeEventCallbackDepth(0)
166 , mNativeEventScheduledDepth(0)
168   // A Cocoa event loop is running here if (and only if) we've been embedded
169   // by a Cocoa app (like Camino).
170   mRunningCocoaEmbedded = [NSApp isRunning] ? true : false;
173 nsAppShell::~nsAppShell()
175   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
177   if (mCFRunLoop) {
178     if (mCFRunLoopSource) {
179       ::CFRunLoopRemoveSource(mCFRunLoop, mCFRunLoopSource,
180                               kCFRunLoopCommonModes);
181       ::CFRelease(mCFRunLoopSource);
182     }
183     ::CFRelease(mCFRunLoop);
184   }
186   if (mAutoreleasePools) {
187     NS_ASSERTION(::CFArrayGetCount(mAutoreleasePools) == 0,
188                  "nsAppShell destroyed without popping all autorelease pools");
189     ::CFRelease(mAutoreleasePools);
190   }
192   [mDelegate release];
194   NS_OBJC_END_TRY_ABORT_BLOCK
197 NS_IMPL_ISUPPORTS(MacWakeLockListener, nsIDOMMozWakeLockListener)
198 mozilla::StaticRefPtr<MacWakeLockListener> sWakeLockListener;
200 static void
201 AddScreenWakeLockListener()
203   nsCOMPtr<nsIPowerManagerService> sPowerManagerService = do_GetService(
204                                                           POWERMANAGERSERVICE_CONTRACTID);
205   if (sPowerManagerService) {
206     sWakeLockListener = new MacWakeLockListener();
207     sPowerManagerService->AddWakeLockListener(sWakeLockListener);
208   } else {
209     NS_WARNING("Failed to retrieve PowerManagerService, wakelocks will be broken!");
210   }
213 static void
214 RemoveScreenWakeLockListener()
216   nsCOMPtr<nsIPowerManagerService> sPowerManagerService = do_GetService(
217                                                           POWERMANAGERSERVICE_CONTRACTID);
218   if (sPowerManagerService) {
219     sPowerManagerService->RemoveWakeLockListener(sWakeLockListener);
220     sPowerManagerService = nullptr;
221     sWakeLockListener = nullptr;
222   }
225 // An undocumented CoreGraphics framework method, present in the same form
226 // since at least OS X 10.5.
227 extern "C" CGError CGSSetDebugOptions(int options);
229 // Init
231 // Loads the nib (see bug 316076c21) and sets up the CFRunLoopSource used to
232 // interrupt the main native run loop.
234 // public
235 nsresult
236 nsAppShell::Init()
238   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
240   // No event loop is running yet (unless Camino is running, or another
241   // embedding app that uses NSApplicationMain()).
242   NSAutoreleasePool* localPool = [[NSAutoreleasePool alloc] init];
244   // mAutoreleasePools is used as a stack of NSAutoreleasePool objects created
245   // by |this|.  CFArray is used instead of NSArray because NSArray wants to
246   // retain each object you add to it, and you can't retain an
247   // NSAutoreleasePool.
248   mAutoreleasePools = ::CFArrayCreateMutable(nullptr, 0, nullptr);
249   NS_ENSURE_STATE(mAutoreleasePools);
251   // Get the path of the nib file, which lives in the GRE location
252   nsCOMPtr<nsIFile> nibFile;
253   nsresult rv = NS_GetSpecialDirectory(NS_GRE_DIR, getter_AddRefs(nibFile));
254   NS_ENSURE_SUCCESS(rv, rv);
256   nibFile->AppendNative(NS_LITERAL_CSTRING("res"));
257   nibFile->AppendNative(NS_LITERAL_CSTRING("MainMenu.nib"));
259   nsAutoCString nibPath;
260   rv = nibFile->GetNativePath(nibPath);
261   NS_ENSURE_SUCCESS(rv, rv);
263   // This call initializes NSApplication unless:
264   // 1) we're using xre -- NSApp's already been initialized by
265   //    MacApplicationDelegate.mm's EnsureUseCocoaDockAPI().
266   // 2) Camino is running (or another embedding app that uses
267   //    NSApplicationMain()) -- NSApp's already been initialized and
268   //    its main run loop is already running.
269   [NSBundle loadNibFile:
270                      [NSString stringWithUTF8String:(const char*)nibPath.get()]
271       externalNameTable:
272            [NSDictionary dictionaryWithObject:[GeckoNSApplication sharedApplication]
273                                        forKey:@"NSOwner"]
274                withZone:NSDefaultMallocZone()];
276   mDelegate = [[AppShellDelegate alloc] initWithAppShell:this];
277   NS_ENSURE_STATE(mDelegate);
279   // Add a CFRunLoopSource to the main native run loop.  The source is
280   // responsible for interrupting the run loop when Gecko events are ready.
282   mCFRunLoop = [[NSRunLoop currentRunLoop] getCFRunLoop];
283   NS_ENSURE_STATE(mCFRunLoop);
284   ::CFRetain(mCFRunLoop);
286   CFRunLoopSourceContext context;
287   bzero(&context, sizeof(context));
288   // context.version = 0;
289   context.info = this;
290   context.perform = ProcessGeckoEvents;
291   
292   mCFRunLoopSource = ::CFRunLoopSourceCreate(kCFAllocatorDefault, 0, &context);
293   NS_ENSURE_STATE(mCFRunLoopSource);
295   ::CFRunLoopAddSource(mCFRunLoop, mCFRunLoopSource, kCFRunLoopCommonModes);
297   rv = nsBaseAppShell::Init();
299 #ifndef __LP64__
300   TextInputHandler::InstallPluginKeyEventsHandler();
301 #endif
303   if (!gAppShellMethodsSwizzled) {
304     // We should only replace the original terminate: method if we're not
305     // running in a Cocoa embedder (like Camino).  See bug 604901.
306     if (!mRunningCocoaEmbedded) {
307       nsToolkit::SwizzleMethods([NSApplication class], @selector(terminate:),
308                                 @selector(nsAppShell_NSApplication_terminate:));
309     }
310     gAppShellMethodsSwizzled = true;
311   }
313   if (nsCocoaFeatures::OnYosemiteOrLater()) {
314     // Explicitly turn off CGEvent logging.  This works around bug 1092855.
315     // If there are already CGEvents in the log, turning off logging also
316     // causes those events to be written to disk.  But at this point no
317     // CGEvents have yet been processed.  CGEvents are events (usually
318     // input events) pulled from the WindowServer.  An option of 0x80000008
319     // turns on CGEvent logging.
320     CGSSetDebugOptions(0x80000007);
321   }
323   [localPool release];
325   return rv;
327   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
330 // ProcessGeckoEvents
332 // The "perform" target of mCFRunLoop, called when mCFRunLoopSource is
333 // signalled from ScheduleNativeEventCallback.
335 // Arrange for Gecko events to be processed on demand (in response to a call
336 // to ScheduleNativeEventCallback(), if processing of Gecko events via "native
337 // methods" hasn't been suspended).  This happens in NativeEventCallback().
339 // protected static
340 void
341 nsAppShell::ProcessGeckoEvents(void* aInfo)
343   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
344   PROFILER_LABEL("Events", "ProcessGeckoEvents",
345     js::ProfileEntry::Category::EVENTS);
347   nsAppShell* self = static_cast<nsAppShell*> (aInfo);
349   if (self->mRunningEventLoop) {
350     self->mRunningEventLoop = false;
352     // The run loop may be sleeping -- [NSRunLoop runMode:...]
353     // won't return until it's given a reason to wake up.  Awaken it by
354     // posting a bogus event.  There's no need to make the event
355     // presentable.
356     //
357     // But _don't_ set windowNumber to '-1' -- that can lead to nasty
358     // wierdness like bmo bug 397039 (a crash in [NSApp sendEvent:] on one of
359     // these fake events, because the -1 has gotten changed into the number
360     // of an actual NSWindow object, and that NSWindow object has just been
361     // destroyed).  Setting windowNumber to '0' seems to work fine -- this
362     // seems to prevent the OS from ever trying to associate our bogus event
363     // with a particular NSWindow object.
364     [NSApp postEvent:[NSEvent otherEventWithType:NSApplicationDefined
365                                         location:NSMakePoint(0,0)
366                                    modifierFlags:0
367                                        timestamp:0
368                                     windowNumber:0
369                                          context:NULL
370                                          subtype:kEventSubtypeNone
371                                            data1:0
372                                            data2:0]
373              atStart:NO];
374   }
376   if (self->mSuspendNativeCount <= 0) {
377     ++self->mNativeEventCallbackDepth;
378     self->NativeEventCallback();
379     --self->mNativeEventCallbackDepth;
380   } else {
381     self->mSkippedNativeCallback = true;
382   }
384   // Still needed to avoid crashes on quit in most Mochitests.
385   [NSApp postEvent:[NSEvent otherEventWithType:NSApplicationDefined
386                                       location:NSMakePoint(0,0)
387                                  modifierFlags:0
388                                      timestamp:0
389                                   windowNumber:0
390                                        context:NULL
391                                        subtype:kEventSubtypeNone
392                                          data1:0
393                                          data2:0]
394            atStart:NO];
396   // Normally every call to ScheduleNativeEventCallback() results in
397   // exactly one call to ProcessGeckoEvents().  So each Release() here
398   // normally balances exactly one AddRef() in ScheduleNativeEventCallback().
399   // But if Exit() is called just after ScheduleNativeEventCallback(), the
400   // corresponding call to ProcessGeckoEvents() will never happen.  We check
401   // for this possibility in two different places -- here and in Exit()
402   // itself.  If we find here that Exit() has been called (that mTerminated
403   // is true), it's because we've been called recursively, that Exit() was
404   // called from self->NativeEventCallback() above, and that we're unwinding
405   // the recursion.  In this case we'll never be called again, and we balance
406   // here any extra calls to ScheduleNativeEventCallback().
407   //
408   // When ProcessGeckoEvents() is called recursively, it's because of a
409   // call to ScheduleNativeEventCallback() from NativeEventCallback().  We
410   // balance the "extra" AddRefs here (rather than always in Exit()) in order
411   // to ensure that 'self' stays alive until the end of this method.  We also
412   // make sure not to finish the balancing until all the recursion has been
413   // unwound.
414   if (self->mTerminated) {
415     int32_t releaseCount = 0;
416     if (self->mNativeEventScheduledDepth > self->mNativeEventCallbackDepth) {
417       releaseCount = PR_ATOMIC_SET(&self->mNativeEventScheduledDepth,
418                                    self->mNativeEventCallbackDepth);
419     }
420     while (releaseCount-- > self->mNativeEventCallbackDepth)
421       self->Release();
422   } else {
423     // As best we can tell, every call to ProcessGeckoEvents() is triggered
424     // by a call to ScheduleNativeEventCallback().  But we've seen a few
425     // (non-reproducible) cases of double-frees that *might* have been caused
426     // by spontaneous calls (from the OS) to ProcessGeckoEvents().  So we
427     // deal with that possibility here.
428     if (PR_ATOMIC_DECREMENT(&self->mNativeEventScheduledDepth) < 0) {
429       PR_ATOMIC_SET(&self->mNativeEventScheduledDepth, 0);
430       NS_WARNING("Spontaneous call to ProcessGeckoEvents()!");
431     } else {
432       self->Release();
433     }
434   }
436   NS_OBJC_END_TRY_ABORT_BLOCK;
439 // WillTerminate
441 // Called by the AppShellDelegate when an NSApplicationWillTerminate
442 // notification is posted.  After this method is called, native events should
443 // no longer be processed.  The NSApplicationWillTerminate notification is
444 // only posted when [NSApp terminate:] is called, which doesn't happen on a
445 // "normal" application quit.
447 // public
448 void
449 nsAppShell::WillTerminate()
451   if (mTerminated)
452     return;
454   // Make sure that the nsAppExitEvent posted by nsAppStartup::Quit() (called
455   // from [MacApplicationDelegate applicationShouldTerminate:]) gets run.
456   NS_ProcessPendingEvents(NS_GetCurrentThread());
458   mTerminated = true;
461 // ScheduleNativeEventCallback
463 // Called (possibly on a non-main thread) when Gecko has an event that
464 // needs to be processed.  The Gecko event needs to be processed on the
465 // main thread, so the native run loop must be interrupted.
467 // In nsBaseAppShell.cpp, the mNativeEventPending variable is used to
468 // ensure that ScheduleNativeEventCallback() is called no more than once
469 // per call to NativeEventCallback().  ProcessGeckoEvents() can skip its
470 // call to NativeEventCallback() if processing of Gecko events by native
471 // means is suspended (using nsIAppShell::SuspendNative()), which will
472 // suspend calls from nsBaseAppShell::OnDispatchedEvent() to
473 // ScheduleNativeEventCallback().  But when Gecko event processing by
474 // native means is resumed (in ResumeNative()), an extra call is made to
475 // ScheduleNativeEventCallback() (from ResumeNative()).  This triggers
476 // another call to ProcessGeckoEvents(), which calls NativeEventCallback(),
477 // and nsBaseAppShell::OnDispatchedEvent() resumes calling
478 // ScheduleNativeEventCallback().
480 // protected virtual
481 void
482 nsAppShell::ScheduleNativeEventCallback()
484   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
486   if (mTerminated)
487     return;
489   // Each AddRef() here is normally balanced by exactly one Release() in
490   // ProcessGeckoEvents().  But there are exceptions, for which see
491   // ProcessGeckoEvents() and Exit().
492   NS_ADDREF_THIS();
493   PR_ATOMIC_INCREMENT(&mNativeEventScheduledDepth);
495   // This will invoke ProcessGeckoEvents on the main thread.
496   ::CFRunLoopSourceSignal(mCFRunLoopSource);
497   ::CFRunLoopWakeUp(mCFRunLoop);
499   NS_OBJC_END_TRY_ABORT_BLOCK;
502 // Undocumented Cocoa Event Manager function, present in the same form since
503 // at least OS X 10.6.
504 extern "C" EventAttributes GetEventAttributes(EventRef inEvent);
506 // ProcessNextNativeEvent
508 // If aMayWait is false, process a single native event.  If it is true, run
509 // the native run loop until stopped by ProcessGeckoEvents.
511 // Returns true if more events are waiting in the native event queue.
513 // protected virtual
514 bool
515 nsAppShell::ProcessNextNativeEvent(bool aMayWait)
517   bool moreEvents = false;
519   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
521   bool eventProcessed = false;
522   NSString* currentMode = nil;
524   if (mTerminated)
525     return false;
527   bool wasRunningEventLoop = mRunningEventLoop;
528   mRunningEventLoop = aMayWait;
529   NSDate* waitUntil = nil;
530   if (aMayWait)
531     waitUntil = [NSDate distantFuture];
533   NSRunLoop* currentRunLoop = [NSRunLoop currentRunLoop];
535   EventQueueRef currentEventQueue = GetCurrentEventQueue();
536   EventTargetRef eventDispatcherTarget = GetEventDispatcherTarget();
538   if (aMayWait) {
539     mozilla::HangMonitor::Suspend();
540   }
542   // Only call -[NSApp sendEvent:] (and indirectly send user-input events to
543   // Gecko) if aMayWait is true.  Tbis ensures most calls to -[NSApp
544   // sendEvent:] happen under nsAppShell::Run(), at the lowest level of
545   // recursion -- thereby making it less likely Gecko will process user-input
546   // events in the wrong order or skip some of them.  It also avoids eating
547   // too much CPU in nsBaseAppShell::OnProcessNextEvent() (which calls
548   // us) -- thereby avoiding the starvation of nsIRunnable events in
549   // nsThread::ProcessNextEvent().  For more information see bug 996848.
550   do {
551     // No autorelease pool is provided here, because OnProcessNextEvent
552     // and AfterProcessNextEvent are responsible for maintaining it.
553     NS_ASSERTION(mAutoreleasePools && ::CFArrayGetCount(mAutoreleasePools),
554                  "No autorelease pool for native event");
556     if (aMayWait) {
557       currentMode = [currentRunLoop currentMode];
558       if (!currentMode)
559         currentMode = NSDefaultRunLoopMode;
560       NSEvent *nextEvent = [NSApp nextEventMatchingMask:NSAnyEventMask
561                                               untilDate:waitUntil
562                                                  inMode:currentMode
563                                                 dequeue:YES];
564       if (nextEvent) {
565         mozilla::HangMonitor::NotifyActivity();
566         [NSApp sendEvent:nextEvent];
567         eventProcessed = true;
568       }
569     } else {
570       // AcquireFirstMatchingEventInQueue() doesn't spin the (native) event
571       // loop, though it does queue up any newly available events from the
572       // window server.
573       EventRef currentEvent = AcquireFirstMatchingEventInQueue(currentEventQueue, 0, NULL,
574                                                                kEventQueueOptionsNone);
575       if (!currentEvent) {
576         continue;
577       }
578       EventAttributes attrs = GetEventAttributes(currentEvent);
579       UInt32 eventKind = GetEventKind(currentEvent);
580       UInt32 eventClass = GetEventClass(currentEvent);
581       bool osCocoaEvent =
582         ((eventClass == 'appl') || (eventClass == kEventClassAppleEvent) ||
583          ((eventClass == 'cgs ') && (eventKind != NSApplicationDefined)));
584       // If attrs is kEventAttributeUserEvent or kEventAttributeMonitored
585       // (i.e. a user input event), we shouldn't process it here while
586       // aMayWait is false.  Likewise if currentEvent will eventually be
587       // turned into an OS-defined Cocoa event, or otherwise needs AppKit
588       // processing.  Doing otherwise risks doing too much work here, and
589       // preventing the event from being properly processed by the AppKit
590       // framework.
591       if ((attrs != kEventAttributeNone) || osCocoaEvent) {
592         // Since we can't process the next event here (while aMayWait is false),
593         // we want moreEvents to be false on return.
594         eventProcessed = false;
595         // This call to ReleaseEvent() matches a call to RetainEvent() in
596         // AcquireFirstMatchingEventInQueue() above.
597         ReleaseEvent(currentEvent);
598         break;
599       }
600       // This call to RetainEvent() matches a call to ReleaseEvent() in
601       // RemoveEventFromQueue() below.
602       RetainEvent(currentEvent);
603       RemoveEventFromQueue(currentEventQueue, currentEvent);
604       SendEventToEventTarget(currentEvent, eventDispatcherTarget);
605       // This call to ReleaseEvent() matches a call to RetainEvent() in
606       // AcquireFirstMatchingEventInQueue() above.
607       ReleaseEvent(currentEvent);
608       eventProcessed = true;
609     }
610   } while (mRunningEventLoop);
612   if (eventProcessed) {
613     moreEvents =
614       (AcquireFirstMatchingEventInQueue(currentEventQueue, 0, NULL,
615                                         kEventQueueOptionsNone) != NULL);
616   }
618   mRunningEventLoop = wasRunningEventLoop;
620   NS_OBJC_END_TRY_ABORT_BLOCK;
622   if (!moreEvents) {
623     nsChildView::UpdateCurrentInputEventCount();
624   }
626   return moreEvents;
629 // Run
631 // Overrides the base class's Run() method to call [NSApp run] (which spins
632 // the native run loop until the application quits).  Since (unlike the base
633 // class's Run() method) we don't process any Gecko events here, they need
634 // to be processed elsewhere (in NativeEventCallback(), called from
635 // ProcessGeckoEvents()).
637 // Camino calls [NSApp run] on its own (via NSApplicationMain()), and so
638 // doesn't call nsAppShell::Run().
640 // public
641 NS_IMETHODIMP
642 nsAppShell::Run(void)
644   NS_ASSERTION(!mStarted, "nsAppShell::Run() called multiple times");
645   if (mStarted || mTerminated)
646     return NS_OK;
648   mStarted = true;
650   AddScreenWakeLockListener();
652   NS_OBJC_TRY_ABORT([NSApp run]);
654   RemoveScreenWakeLockListener();
656   return NS_OK;
659 NS_IMETHODIMP
660 nsAppShell::Exit(void)
662   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
664   // This method is currently called more than once -- from (according to
665   // mento) an nsAppExitEvent dispatched by nsAppStartup::Quit() and from an
666   // XPCOM shutdown notification that nsBaseAppShell has registered to
667   // receive.  So we need to ensure that multiple calls won't break anything.
668   // But we should also complain about it (since it isn't quite kosher).
669   if (mTerminated) {
670     NS_WARNING("nsAppShell::Exit() called redundantly");
671     return NS_OK;
672   }
674   mTerminated = true;
676 #ifndef __LP64__
677   TextInputHandler::RemovePluginKeyEventsHandler();
678 #endif
680   // Quoting from Apple's doc on the [NSApplication stop:] method (from their
681   // doc on the NSApplication class):  "If this method is invoked during a
682   // modal event loop, it will break that loop but not the main event loop."
683   // nsAppShell::Exit() shouldn't be called from a modal event loop.  So if
684   // it is we complain about it (to users of debug builds) and call [NSApp
685   // stop:] one extra time.  (I'm not sure if modal event loops can be nested
686   // -- Apple's docs don't say one way or the other.  But the return value
687   // of [NSApp _isRunningModal] doesn't change immediately after a call to
688   // [NSApp stop:], so we have to assume that one extra call to [NSApp stop:]
689   // will do the job.)
690   BOOL cocoaModal = [NSApp _isRunningModal];
691   NS_ASSERTION(!cocoaModal,
692                "Don't call nsAppShell::Exit() from a modal event loop!");
693   if (cocoaModal)
694     [NSApp stop:nullptr];
695   [NSApp stop:nullptr];
697   // A call to Exit() just after a call to ScheduleNativeEventCallback()
698   // prevents the (normally) matching call to ProcessGeckoEvents() from
699   // happening.  If we've been called from ProcessGeckoEvents() (as usually
700   // happens), we take care of it there.  But if we have an unbalanced call
701   // to ScheduleNativeEventCallback() and ProcessGeckoEvents() isn't on the
702   // stack, we need to take care of the problem here.
703   if (!mNativeEventCallbackDepth && mNativeEventScheduledDepth) {
704     int32_t releaseCount = PR_ATOMIC_SET(&mNativeEventScheduledDepth, 0);
705     while (releaseCount-- > 0)
706       NS_RELEASE_THIS();
707   }
709   return nsBaseAppShell::Exit();
711   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
714 // OnProcessNextEvent
716 // This nsIThreadObserver method is called prior to processing an event.
717 // Set up an autorelease pool that will service any autoreleased Cocoa
718 // objects during this event.  This includes native events processed by
719 // ProcessNextNativeEvent.  The autorelease pool will be popped by
720 // AfterProcessNextEvent, it is important for these two methods to be
721 // tightly coupled.
723 // public
724 NS_IMETHODIMP
725 nsAppShell::OnProcessNextEvent(nsIThreadInternal *aThread, bool aMayWait,
726                                uint32_t aRecursionDepth)
728   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
730   NS_ASSERTION(mAutoreleasePools,
731                "No stack on which to store autorelease pool");
733   NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
734   ::CFArrayAppendValue(mAutoreleasePools, pool);
736   return nsBaseAppShell::OnProcessNextEvent(aThread, aMayWait, aRecursionDepth);
738   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
741 // AfterProcessNextEvent
743 // This nsIThreadObserver method is called after event processing is complete.
744 // The Cocoa implementation cleans up the autorelease pool create by the
745 // previous OnProcessNextEvent call.
747 // public
748 NS_IMETHODIMP
749 nsAppShell::AfterProcessNextEvent(nsIThreadInternal *aThread,
750                                   uint32_t aRecursionDepth,
751                                   bool aEventWasProcessed)
753   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
755   CFIndex count = ::CFArrayGetCount(mAutoreleasePools);
757   NS_ASSERTION(mAutoreleasePools && count,
758                "Processed an event, but there's no autorelease pool?");
760   const NSAutoreleasePool* pool = static_cast<const NSAutoreleasePool*>
761     (::CFArrayGetValueAtIndex(mAutoreleasePools, count - 1));
762   ::CFArrayRemoveValueAtIndex(mAutoreleasePools, count - 1);
763   [pool release];
765   return nsBaseAppShell::AfterProcessNextEvent(aThread, aRecursionDepth,
766                                                aEventWasProcessed);
768   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
772 // AppShellDelegate implementation
775 @implementation AppShellDelegate
776 // initWithAppShell:
778 // Constructs the AppShellDelegate object
779 - (id)initWithAppShell:(nsAppShell*)aAppShell
781   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
783   if ((self = [self init])) {
784     mAppShell = aAppShell;
786     [[NSNotificationCenter defaultCenter] addObserver:self
787                                              selector:@selector(applicationWillTerminate:)
788                                                  name:NSApplicationWillTerminateNotification
789                                                object:NSApp];
790     [[NSNotificationCenter defaultCenter] addObserver:self
791                                              selector:@selector(applicationDidBecomeActive:)
792                                                  name:NSApplicationDidBecomeActiveNotification
793                                                object:NSApp];
794     [[NSDistributedNotificationCenter defaultCenter] addObserver:self
795                                                         selector:@selector(beginMenuTracking:)
796                                                             name:@"com.apple.HIToolbox.beginMenuTrackingNotification"
797                                                           object:nil];
798   }
800   return self;
802   NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
805 - (void)dealloc
807   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
809   [[NSNotificationCenter defaultCenter] removeObserver:self];
810   [[NSDistributedNotificationCenter defaultCenter] removeObserver:self];
811   [super dealloc];
813   NS_OBJC_END_TRY_ABORT_BLOCK;
816 // applicationWillTerminate:
818 // Notify the nsAppShell that native event processing should be discontinued.
819 - (void)applicationWillTerminate:(NSNotification*)aNotification
821   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
823   mAppShell->WillTerminate();
825   NS_OBJC_END_TRY_ABORT_BLOCK;
828 // applicationDidBecomeActive
830 // Make sure TextInputHandler::sLastModifierState is updated when we become
831 // active (since we won't have received [ChildView flagsChanged:] messages
832 // while inactive).
833 - (void)applicationDidBecomeActive:(NSNotification*)aNotification
835   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
837   // [NSEvent modifierFlags] is valid on every kind of event, so we don't need
838   // to worry about getting an NSInternalInconsistencyException here.
839   NSEvent* currentEvent = [NSApp currentEvent];
840   if (currentEvent) {
841     TextInputHandler::sLastModifierState =
842       [currentEvent modifierFlags] & NSDeviceIndependentModifierFlagsMask;
843   }
845   NS_OBJC_END_TRY_ABORT_BLOCK;
848 // beginMenuTracking
850 // Roll up our context menu (if any) when some other app (or the OS) opens
851 // any sort of menu.  But make sure we don't do this for notifications we
852 // send ourselves (whose 'sender' will be @"org.mozilla.gecko.PopupWindow").
853 - (void)beginMenuTracking:(NSNotification*)aNotification
855   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
857   NSString *sender = [aNotification object];
858   if (!sender || ![sender isEqualToString:@"org.mozilla.gecko.PopupWindow"]) {
859     nsIRollupListener* rollupListener = nsBaseWidget::GetActiveRollupListener();
860     nsCOMPtr<nsIWidget> rollupWidget = rollupListener->GetRollupWidget();
861     if (rollupWidget)
862       rollupListener->Rollup(0, nullptr, nullptr);
863   }
865   NS_OBJC_END_TRY_ABORT_BLOCK;
868 @end
870 // We hook terminate: in order to make OS-initiated termination work nicely
871 // with Gecko's shutdown sequence.  (Two ways to trigger OS-initiated
872 // termination:  1) Quit from the Dock menu; 2) Log out from (or shut down)
873 // your computer while the browser is active.)
874 @interface NSApplication (MethodSwizzling)
875 - (void)nsAppShell_NSApplication_terminate:(id)sender;
876 @end
878 @implementation NSApplication (MethodSwizzling)
880 // Called by the OS after [MacApplicationDelegate applicationShouldTerminate:]
881 // has returned NSTerminateNow.  This method "subclasses" and replaces the
882 // OS's original implementation.  The only thing the orginal method does which
883 // we need is that it posts NSApplicationWillTerminateNotification.  Everything
884 // else is unneeded (because it's handled elsewhere), or actively interferes
885 // with Gecko's shutdown sequence.  For example the original terminate: method
886 // causes the app to exit() inside [NSApp run] (called from nsAppShell::Run()
887 // above), which means that nothing runs after the call to nsAppStartup::Run()
888 // in XRE_Main(), which in particular means that ScopedXPCOMStartup's destructor
889 // and NS_ShutdownXPCOM() never get called.
890 - (void)nsAppShell_NSApplication_terminate:(id)sender
892   [[NSNotificationCenter defaultCenter] postNotificationName:NSApplicationWillTerminateNotification
893                                                       object:NSApp];
896 @end