forked from mirrors/gecko-dev
		
	 06d8bf65ac
			
		
	
	
		06d8bf65ac
		
	
	
	
	
		
			
			Same as other platforms. Differential Revision: https://phabricator.services.mozilla.com/D209795
		
			
				
	
	
		
			493 lines
		
	
	
	
		
			16 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			493 lines
		
	
	
	
		
			16 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 | |
| /* vim: set ts=2 et sw=2 tw=80: */
 | |
| /* This Source Code Form is subject to the terms of the Mozilla Public
 | |
|  * License, v. 2.0. If a copy of the MPL was not distributed with this
 | |
|  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 | |
| 
 | |
| #include <sys/types.h>
 | |
| #include <unistd.h>
 | |
| #include <fcntl.h>
 | |
| #include <errno.h>
 | |
| #include <gdk/gdk.h>
 | |
| #include "nsAppShell.h"
 | |
| #include "nsBaseAppShell.h"
 | |
| #include "nsWindow.h"
 | |
| #include "mozilla/Logging.h"
 | |
| #include "prenv.h"
 | |
| #include "mozilla/BackgroundHangMonitor.h"
 | |
| #include "mozilla/Hal.h"
 | |
| #include "mozilla/ProfilerLabels.h"
 | |
| #include "mozilla/ProfilerThreadSleep.h"
 | |
| #include "mozilla/Unused.h"
 | |
| #include "mozilla/GUniquePtr.h"
 | |
| #include "mozilla/WidgetUtils.h"
 | |
| #include "nsIPowerManagerService.h"
 | |
| #ifdef MOZ_ENABLE_DBUS
 | |
| #  include <gio/gio.h>
 | |
| #  include "nsIObserverService.h"
 | |
| #  include "WidgetUtilsGtk.h"
 | |
| #endif
 | |
| #include "WakeLockListener.h"
 | |
| #include "gfxPlatform.h"
 | |
| #include "nsAppRunner.h"
 | |
| #include "mozilla/XREAppData.h"
 | |
| #include "ScreenHelperGTK.h"
 | |
| #include "HeadlessScreenHelper.h"
 | |
| #include "mozilla/widget/ScreenManager.h"
 | |
| #ifdef MOZ_WAYLAND
 | |
| #  include "nsWaylandDisplay.h"
 | |
| #endif
 | |
| 
 | |
| using namespace mozilla;
 | |
| using namespace mozilla::widget;
 | |
| using mozilla::widget::HeadlessScreenHelper;
 | |
| using mozilla::widget::ScreenHelperGTK;
 | |
| using mozilla::widget::ScreenManager;
 | |
| 
 | |
| #define NOTIFY_TOKEN 0xFA
 | |
| #define QUIT_TOKEN 0xFB
 | |
| 
 | |
| LazyLogModule gWidgetLog("Widget");
 | |
| LazyLogModule gWidgetDragLog("WidgetDrag");
 | |
| LazyLogModule gWidgetWaylandLog("WidgetWayland");
 | |
| LazyLogModule gWidgetPopupLog("WidgetPopup");
 | |
| LazyLogModule gWidgetVsync("WidgetVsync");
 | |
| LazyLogModule gDmabufLog("Dmabuf");
 | |
| 
 | |
| static GPollFunc sPollFunc;
 | |
| 
 | |
| nsAppShell* sAppShell = nullptr;
 | |
| 
 | |
| // Wrapper function to disable hang monitoring while waiting in poll().
 | |
| static gint PollWrapper(GPollFD* aUfds, guint aNfsd, gint aTimeout) {
 | |
|   if (aTimeout == 0) {
 | |
|     // When the timeout is 0, there is no wait, so no point in notifying
 | |
|     // the BackgroundHangMonitor and the profiler.
 | |
|     return (*sPollFunc)(aUfds, aNfsd, aTimeout);
 | |
|   }
 | |
| 
 | |
|   mozilla::BackgroundHangMonitor().NotifyWait();
 | |
|   gint result;
 | |
|   {
 | |
|     gint timeout = aTimeout;
 | |
|     gint64 begin = 0;
 | |
|     if (aTimeout != -1) {
 | |
|       begin = g_get_monotonic_time();
 | |
|     }
 | |
| 
 | |
|     AUTO_PROFILER_LABEL("PollWrapper", IDLE);
 | |
|     AUTO_PROFILER_THREAD_SLEEP;
 | |
|     do {
 | |
|       result = (*sPollFunc)(aUfds, aNfsd, timeout);
 | |
| 
 | |
|       // The result will be -1 with the EINTR error if the poll was interrupted
 | |
|       // by a signal, typically the signal sent by the profiler to sample the
 | |
|       // process. We are only done waiting if we are not in that case.
 | |
|       if (result != -1 || errno != EINTR) {
 | |
|         break;
 | |
|       }
 | |
| 
 | |
|       if (aTimeout != -1) {
 | |
|         // Adjust the timeout to account for the time already spent waiting.
 | |
|         gint elapsedSinceBegin = (g_get_monotonic_time() - begin) / 1000;
 | |
|         if (elapsedSinceBegin < aTimeout) {
 | |
|           timeout = aTimeout - elapsedSinceBegin;
 | |
|         } else {
 | |
|           // poll returns 0 to indicate the call timed out before any fd
 | |
|           // became ready.
 | |
|           result = 0;
 | |
|           break;
 | |
|         }
 | |
|       }
 | |
|     } while (true);
 | |
|   }
 | |
|   mozilla::BackgroundHangMonitor().NotifyActivity();
 | |
|   return result;
 | |
| }
 | |
| 
 | |
| // Emit resume-events on GdkFrameClock if flush-events has not been
 | |
| // balanced by resume-events at dispose.
 | |
| // For https://bugzilla.gnome.org/show_bug.cgi?id=742636
 | |
| static decltype(GObjectClass::constructed) sRealGdkFrameClockConstructed;
 | |
| static decltype(GObjectClass::dispose) sRealGdkFrameClockDispose;
 | |
| static GQuark sPendingResumeQuark;
 | |
| 
 | |
| static void OnFlushEvents(GObject* clock, gpointer) {
 | |
|   g_object_set_qdata(clock, sPendingResumeQuark, GUINT_TO_POINTER(1));
 | |
| }
 | |
| 
 | |
| static void OnResumeEvents(GObject* clock, gpointer) {
 | |
|   g_object_set_qdata(clock, sPendingResumeQuark, nullptr);
 | |
| }
 | |
| 
 | |
| static void WrapGdkFrameClockConstructed(GObject* object) {
 | |
|   sRealGdkFrameClockConstructed(object);
 | |
| 
 | |
|   g_signal_connect(object, "flush-events", G_CALLBACK(OnFlushEvents), nullptr);
 | |
|   g_signal_connect(object, "resume-events", G_CALLBACK(OnResumeEvents),
 | |
|                    nullptr);
 | |
| }
 | |
| 
 | |
| static void WrapGdkFrameClockDispose(GObject* object) {
 | |
|   if (g_object_get_qdata(object, sPendingResumeQuark)) {
 | |
|     g_signal_emit_by_name(object, "resume-events");
 | |
|   }
 | |
| 
 | |
|   sRealGdkFrameClockDispose(object);
 | |
| }
 | |
| 
 | |
| /*static*/
 | |
| gboolean nsAppShell::EventProcessorCallback(GIOChannel* source,
 | |
|                                             GIOCondition condition,
 | |
|                                             gpointer data) {
 | |
|   nsAppShell* self = static_cast<nsAppShell*>(data);
 | |
| 
 | |
|   unsigned char c;
 | |
|   Unused << read(self->mPipeFDs[0], &c, 1);
 | |
|   switch (c) {
 | |
|     case NOTIFY_TOKEN:
 | |
|       self->NativeEventCallback();
 | |
|       break;
 | |
|     case QUIT_TOKEN:
 | |
|       self->Exit();
 | |
|       break;
 | |
|     default:
 | |
|       NS_ASSERTION(false, "wrong token");
 | |
|       break;
 | |
|   }
 | |
|   return TRUE;
 | |
| }
 | |
| 
 | |
| nsAppShell::~nsAppShell() {
 | |
|   sAppShell = nullptr;
 | |
| 
 | |
| #ifdef MOZ_ENABLE_DBUS
 | |
|   StopDBusListening();
 | |
| #endif
 | |
|   mozilla::hal::Shutdown();
 | |
| 
 | |
|   if (mTag) g_source_remove(mTag);
 | |
|   if (mPipeFDs[0]) close(mPipeFDs[0]);
 | |
|   if (mPipeFDs[1]) close(mPipeFDs[1]);
 | |
| }
 | |
| 
 | |
| mozilla::StaticRefPtr<WakeLockListener> sWakeLockListener;
 | |
| static void AddScreenWakeLockListener() {
 | |
|   nsCOMPtr<nsIPowerManagerService> powerManager =
 | |
|       do_GetService(POWERMANAGERSERVICE_CONTRACTID);
 | |
|   if (powerManager) {
 | |
|     sWakeLockListener = new WakeLockListener();
 | |
|     powerManager->AddWakeLockListener(sWakeLockListener);
 | |
|   } else {
 | |
|     NS_WARNING(
 | |
|         "Failed to retrieve PowerManagerService, wakelocks will be broken!");
 | |
|   }
 | |
| }
 | |
| 
 | |
| static void RemoveScreenWakeLockListener() {
 | |
|   nsCOMPtr<nsIPowerManagerService> powerManager =
 | |
|       do_GetService(POWERMANAGERSERVICE_CONTRACTID);
 | |
|   if (powerManager) {
 | |
|     powerManager->RemoveWakeLockListener(sWakeLockListener);
 | |
|     sWakeLockListener = nullptr;
 | |
|   }
 | |
| }
 | |
| 
 | |
| #ifdef MOZ_ENABLE_DBUS
 | |
| void nsAppShell::DBusSessionSleepCallback(GDBusProxy* aProxy,
 | |
|                                           gchar* aSenderName,
 | |
|                                           gchar* aSignalName,
 | |
|                                           GVariant* aParameters,
 | |
|                                           gpointer aUserData) {
 | |
|   if (g_strcmp0(aSignalName, "PrepareForSleep")) {
 | |
|     return;
 | |
|   }
 | |
|   nsCOMPtr<nsIObserverService> observerService =
 | |
|       mozilla::services::GetObserverService();
 | |
|   if (!observerService) {
 | |
|     return;
 | |
|   }
 | |
|   if (!g_variant_is_of_type(aParameters, G_VARIANT_TYPE_TUPLE) ||
 | |
|       g_variant_n_children(aParameters) != 1) {
 | |
|     NS_WARNING(
 | |
|         nsPrintfCString("Unexpected location updated signal params type: %s\n",
 | |
|                         g_variant_get_type_string(aParameters))
 | |
|             .get());
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   RefPtr<GVariant> variant =
 | |
|       dont_AddRef(g_variant_get_child_value(aParameters, 0));
 | |
|   if (!g_variant_is_of_type(variant, G_VARIANT_TYPE_BOOLEAN)) {
 | |
|     NS_WARNING(
 | |
|         nsPrintfCString("Unexpected location updated signal params type: %s\n",
 | |
|                         g_variant_get_type_string(aParameters))
 | |
|             .get());
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   gboolean suspend = g_variant_get_boolean(variant);
 | |
|   if (suspend) {
 | |
|     // Post sleep_notification
 | |
|     observerService->NotifyObservers(nullptr, NS_WIDGET_SLEEP_OBSERVER_TOPIC,
 | |
|                                      nullptr);
 | |
|   } else {
 | |
|     // Post wake_notification
 | |
|     observerService->NotifyObservers(nullptr, NS_WIDGET_WAKE_OBSERVER_TOPIC,
 | |
|                                      nullptr);
 | |
|   }
 | |
| }
 | |
| 
 | |
| void nsAppShell::DBusTimedatePropertiesChangedCallback(GDBusProxy* aProxy,
 | |
|                                                        gchar* aSenderName,
 | |
|                                                        gchar* aSignalName,
 | |
|                                                        GVariant* aParameters,
 | |
|                                                        gpointer aUserData) {
 | |
|   if (g_strcmp0(aSignalName, "PropertiesChanged")) {
 | |
|     return;
 | |
|   }
 | |
|   nsBaseAppShell::OnSystemTimezoneChange();
 | |
| }
 | |
| 
 | |
| void nsAppShell::DBusConnectClientResponse(GObject* aObject,
 | |
|                                            GAsyncResult* aResult,
 | |
|                                            gpointer aUserData) {
 | |
|   GUniquePtr<GError> error;
 | |
|   RefPtr<GDBusProxy> proxyClient =
 | |
|       dont_AddRef(g_dbus_proxy_new_finish(aResult, getter_Transfers(error)));
 | |
|   if (!proxyClient) {
 | |
|     if (!IsCancelledGError(error.get())) {
 | |
|       NS_WARNING(
 | |
|           nsPrintfCString("Failed to connect to client: %s\n", error->message)
 | |
|               .get());
 | |
|     }
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   RefPtr self = static_cast<nsAppShell*>(aUserData);
 | |
|   if (!strcmp(g_dbus_proxy_get_name(proxyClient), "org.freedesktop.login1")) {
 | |
|     self->mLogin1Proxy = std::move(proxyClient);
 | |
|     g_signal_connect(self->mLogin1Proxy, "g-signal",
 | |
|                      G_CALLBACK(DBusSessionSleepCallback), self);
 | |
|   } else {
 | |
|     self->mTimedate1Proxy = std::move(proxyClient);
 | |
|     g_signal_connect(self->mTimedate1Proxy, "g-signal",
 | |
|                      G_CALLBACK(DBusTimedatePropertiesChangedCallback), self);
 | |
|   }
 | |
| }
 | |
| 
 | |
| // Based on
 | |
| // https://github.com/lcp/NetworkManager/blob/240f47c892b4e935a3e92fc09eb15163d1fa28d8/src/nm-sleep-monitor-systemd.c
 | |
| // Use login1 to signal sleep and wake notifications.
 | |
| void nsAppShell::StartDBusListening() {
 | |
|   MOZ_DIAGNOSTIC_ASSERT(!mLogin1Proxy, "Already configured?");
 | |
|   MOZ_DIAGNOSTIC_ASSERT(!mTimedate1Proxy, "Already configured?");
 | |
|   MOZ_DIAGNOSTIC_ASSERT(!mLogin1ProxyCancellable, "Already configured?");
 | |
|   MOZ_DIAGNOSTIC_ASSERT(!mTimedate1ProxyCancellable, "Already configured?");
 | |
| 
 | |
|   mLogin1ProxyCancellable = dont_AddRef(g_cancellable_new());
 | |
|   mTimedate1ProxyCancellable = dont_AddRef(g_cancellable_new());
 | |
| 
 | |
|   g_dbus_proxy_new_for_bus(
 | |
|       G_BUS_TYPE_SYSTEM, G_DBUS_PROXY_FLAGS_NONE, nullptr,
 | |
|       "org.freedesktop.login1", "/org/freedesktop/login1",
 | |
|       "org.freedesktop.login1.Manager", mLogin1ProxyCancellable,
 | |
|       reinterpret_cast<GAsyncReadyCallback>(DBusConnectClientResponse), this);
 | |
| 
 | |
|   g_dbus_proxy_new_for_bus(
 | |
|       G_BUS_TYPE_SYSTEM, G_DBUS_PROXY_FLAGS_NONE, nullptr,
 | |
|       "org.freedesktop.timedate1", "/org/freedesktop/timedate1",
 | |
|       "org.freedesktop.DBus.Properties", mTimedate1ProxyCancellable,
 | |
|       reinterpret_cast<GAsyncReadyCallback>(DBusConnectClientResponse), this);
 | |
| }
 | |
| 
 | |
| void nsAppShell::StopDBusListening() {
 | |
|   if (mLogin1Proxy) {
 | |
|     g_signal_handlers_disconnect_matched(mLogin1Proxy, G_SIGNAL_MATCH_DATA, 0,
 | |
|                                          0, nullptr, nullptr, this);
 | |
|   }
 | |
|   if (mLogin1ProxyCancellable) {
 | |
|     g_cancellable_cancel(mLogin1ProxyCancellable);
 | |
|     mLogin1ProxyCancellable = nullptr;
 | |
|   }
 | |
|   mLogin1Proxy = nullptr;
 | |
| 
 | |
|   if (mTimedate1Proxy) {
 | |
|     g_signal_handlers_disconnect_matched(mTimedate1Proxy, G_SIGNAL_MATCH_DATA,
 | |
|                                          0, 0, nullptr, nullptr, this);
 | |
|   }
 | |
|   if (mTimedate1ProxyCancellable) {
 | |
|     g_cancellable_cancel(mTimedate1ProxyCancellable);
 | |
|     mTimedate1ProxyCancellable = nullptr;
 | |
|   }
 | |
|   mTimedate1Proxy = nullptr;
 | |
| }
 | |
| #endif
 | |
| 
 | |
| void nsAppShell::TermSignalHandler(int signo) {
 | |
|   if (signo != SIGTERM) {
 | |
|     NS_WARNING("Wrong signal!");
 | |
|     return;
 | |
|   }
 | |
|   sAppShell->ScheduleQuitEvent();
 | |
| }
 | |
| 
 | |
| void nsAppShell::InstallTermSignalHandler() {
 | |
|   if (!XRE_IsParentProcess() || PR_GetEnv("MOZ_DISABLE_SIG_HANDLER") ||
 | |
|       !sAppShell) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   struct sigaction act = {}, oldact;
 | |
|   act.sa_handler = TermSignalHandler;
 | |
|   sigfillset(&act.sa_mask);
 | |
| 
 | |
|   if (NS_WARN_IF(sigaction(SIGTERM, nullptr, &oldact) != 0)) {
 | |
|     return;
 | |
|   }
 | |
|   if (oldact.sa_handler != SIG_DFL) {
 | |
|     NS_WARNING("SIGTERM signal handler is already set?");
 | |
|   }
 | |
| 
 | |
|   sigaction(SIGTERM, &act, nullptr);
 | |
| }
 | |
| 
 | |
| nsresult nsAppShell::Init() {
 | |
|   mozilla::hal::Init();
 | |
| 
 | |
| #ifdef MOZ_ENABLE_DBUS
 | |
|   if (XRE_IsParentProcess()) {
 | |
|     StartDBusListening();
 | |
|   }
 | |
| #endif
 | |
| 
 | |
|   if (!sPollFunc) {
 | |
|     sPollFunc = g_main_context_get_poll_func(nullptr);
 | |
|     g_main_context_set_poll_func(nullptr, &PollWrapper);
 | |
|   }
 | |
| 
 | |
|   if (XRE_IsParentProcess()) {
 | |
|     ScreenManager& screenManager = ScreenManager::GetSingleton();
 | |
|     if (gfxPlatform::IsHeadless()) {
 | |
|       screenManager.SetHelper(mozilla::MakeUnique<HeadlessScreenHelper>());
 | |
|     } else {
 | |
|       screenManager.SetHelper(mozilla::MakeUnique<ScreenHelperGTK>());
 | |
|     }
 | |
| 
 | |
|     if (gtk_check_version(3, 16, 3) == nullptr) {
 | |
|       // Before 3.16.3, GDK cannot override classname by --class command line
 | |
|       // option when program uses gdk_set_program_class().
 | |
|       //
 | |
|       // See https://bugzilla.gnome.org/show_bug.cgi?id=747634
 | |
|       //
 | |
|       // Only bother doing this for the parent process, since it's the one
 | |
|       // creating top-level windows.
 | |
|       if (gAppData) {
 | |
|         gdk_set_program_class(gAppData->remotingName);
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   if (!sPendingResumeQuark &&
 | |
|       gtk_check_version(3, 14, 7) != nullptr) {  // GTK 3.0 to GTK 3.14.7.
 | |
|     // GTK 3.8 - 3.14 registered this type when creating the frame clock
 | |
|     // for the root window of the display when the display was opened.
 | |
|     GType gdkFrameClockIdleType = g_type_from_name("GdkFrameClockIdle");
 | |
|     if (gdkFrameClockIdleType) {  // not in versions prior to 3.8
 | |
|       sPendingResumeQuark = g_quark_from_string("moz-resume-is-pending");
 | |
|       auto gdk_frame_clock_idle_class =
 | |
|           G_OBJECT_CLASS(g_type_class_peek_static(gdkFrameClockIdleType));
 | |
|       auto constructed = &gdk_frame_clock_idle_class->constructed;
 | |
|       sRealGdkFrameClockConstructed = *constructed;
 | |
|       *constructed = WrapGdkFrameClockConstructed;
 | |
|       auto dispose = &gdk_frame_clock_idle_class->dispose;
 | |
|       sRealGdkFrameClockDispose = *dispose;
 | |
|       *dispose = WrapGdkFrameClockDispose;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   // Workaround for bug 1209659 which is fixed by Gtk3.20
 | |
|   if (gtk_check_version(3, 20, 0) != nullptr) {
 | |
|     unsetenv("GTK_CSD");
 | |
|   }
 | |
| 
 | |
|   // Whitelist of only common, stable formats - see bugs 1197059 and 1203078
 | |
|   GSList* pixbufFormats = gdk_pixbuf_get_formats();
 | |
|   for (GSList* iter = pixbufFormats; iter; iter = iter->next) {
 | |
|     GdkPixbufFormat* format = static_cast<GdkPixbufFormat*>(iter->data);
 | |
|     gchar* name = gdk_pixbuf_format_get_name(format);
 | |
|     if (strcmp(name, "jpeg") && strcmp(name, "png") && strcmp(name, "gif") &&
 | |
|         strcmp(name, "bmp") && strcmp(name, "ico") && strcmp(name, "xpm") &&
 | |
|         strcmp(name, "svg") && strcmp(name, "webp") && strcmp(name, "avif")) {
 | |
|       gdk_pixbuf_format_set_disabled(format, TRUE);
 | |
|     }
 | |
|     g_free(name);
 | |
|   }
 | |
|   g_slist_free(pixbufFormats);
 | |
| 
 | |
|   int err = pipe(mPipeFDs);
 | |
|   if (err) return NS_ERROR_OUT_OF_MEMORY;
 | |
| 
 | |
|   GIOChannel* ioc;
 | |
|   GSource* source;
 | |
| 
 | |
|   // make the pipe nonblocking
 | |
| 
 | |
|   int flags = fcntl(mPipeFDs[0], F_GETFL, 0);
 | |
|   if (flags == -1) goto failed;
 | |
|   err = fcntl(mPipeFDs[0], F_SETFL, flags | O_NONBLOCK);
 | |
|   if (err == -1) goto failed;
 | |
|   flags = fcntl(mPipeFDs[1], F_GETFL, 0);
 | |
|   if (flags == -1) goto failed;
 | |
|   err = fcntl(mPipeFDs[1], F_SETFL, flags | O_NONBLOCK);
 | |
|   if (err == -1) goto failed;
 | |
| 
 | |
|   ioc = g_io_channel_unix_new(mPipeFDs[0]);
 | |
|   source = g_io_create_watch(ioc, G_IO_IN);
 | |
|   g_io_channel_unref(ioc);
 | |
|   g_source_set_callback(source, (GSourceFunc)EventProcessorCallback, this,
 | |
|                         nullptr);
 | |
|   g_source_set_can_recurse(source, TRUE);
 | |
|   mTag = g_source_attach(source, nullptr);
 | |
|   g_source_unref(source);
 | |
| 
 | |
|   sAppShell = this;
 | |
| 
 | |
|   return nsBaseAppShell::Init();
 | |
| failed:
 | |
|   close(mPipeFDs[0]);
 | |
|   close(mPipeFDs[1]);
 | |
|   mPipeFDs[0] = mPipeFDs[1] = 0;
 | |
|   return NS_ERROR_FAILURE;
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP nsAppShell::Run() {
 | |
|   if (XRE_IsParentProcess()) {
 | |
|     AddScreenWakeLockListener();
 | |
|   }
 | |
| 
 | |
|   nsresult rv = nsBaseAppShell::Run();
 | |
| 
 | |
|   if (XRE_IsParentProcess()) {
 | |
|     RemoveScreenWakeLockListener();
 | |
|   }
 | |
|   return rv;
 | |
| }
 | |
| 
 | |
| void nsAppShell::ScheduleNativeEventCallback() {
 | |
|   unsigned char buf[] = {NOTIFY_TOKEN};
 | |
|   Unused << write(mPipeFDs[1], buf, 1);
 | |
| }
 | |
| 
 | |
| void nsAppShell::ScheduleQuitEvent() {
 | |
|   unsigned char buf[] = {QUIT_TOKEN};
 | |
|   Unused << write(mPipeFDs[1], buf, 1);
 | |
| }
 | |
| 
 | |
| bool nsAppShell::ProcessNextNativeEvent(bool mayWait) {
 | |
|   if (mSuspendNativeCount) {
 | |
|     return false;
 | |
|   }
 | |
|   bool didProcessEvent = g_main_context_iteration(nullptr, mayWait);
 | |
|   return didProcessEvent;
 | |
| }
 |