forked from mirrors/gecko-dev
		
	 9982a76fe9
			
		
	
	
		9982a76fe9
		
	
	
	
	
		
			
			Better to try inhibit native wake lock again that to switch to Unsupported one and disable it at all. Differential Revision: https://phabricator.services.mozilla.com/D201709
		
			
				
	
	
		
			935 lines
		
	
	
	
		
			30 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			935 lines
		
	
	
	
		
			30 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 | |
| /* vim:expandtab:shiftwidth=2:tabstop=2:
 | |
|  */
 | |
| /* 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 "WakeLockListener.h"
 | |
| #include "WidgetUtilsGtk.h"
 | |
| #include "mozilla/ScopeExit.h"
 | |
| 
 | |
| #ifdef MOZ_ENABLE_DBUS
 | |
| #  include <gio/gio.h>
 | |
| #  include "AsyncDBus.h"
 | |
| #endif
 | |
| 
 | |
| #if defined(MOZ_X11)
 | |
| #  include "prlink.h"
 | |
| #  include <gdk/gdk.h>
 | |
| #  include <gdk/gdkx.h>
 | |
| #  include "X11UndefineNone.h"
 | |
| #endif
 | |
| 
 | |
| #if defined(MOZ_WAYLAND)
 | |
| #  include "mozilla/widget/nsWaylandDisplay.h"
 | |
| #  include "nsWindow.h"
 | |
| #endif
 | |
| 
 | |
| #ifdef MOZ_ENABLE_DBUS
 | |
| #  define FREEDESKTOP_PORTAL_DESKTOP_TARGET "org.freedesktop.portal.Desktop"
 | |
| #  define FREEDESKTOP_PORTAL_DESKTOP_OBJECT "/org/freedesktop/portal/desktop"
 | |
| #  define FREEDESKTOP_PORTAL_DESKTOP_INTERFACE "org.freedesktop.portal.Inhibit"
 | |
| #  define FREEDESKTOP_PORTAL_DESKTOP_INHIBIT_IDLE_FLAG 8
 | |
| 
 | |
| #  define FREEDESKTOP_SCREENSAVER_TARGET "org.freedesktop.ScreenSaver"
 | |
| #  define FREEDESKTOP_SCREENSAVER_OBJECT "/ScreenSaver"
 | |
| #  define FREEDESKTOP_SCREENSAVER_INTERFACE "org.freedesktop.ScreenSaver"
 | |
| 
 | |
| #  define FREEDESKTOP_POWER_TARGET "org.freedesktop.PowerManagement"
 | |
| #  define FREEDESKTOP_POWER_OBJECT "/org/freedesktop/PowerManagement/Inhibit"
 | |
| #  define FREEDESKTOP_POWER_INTERFACE "org.freedesktop.PowerManagement.Inhibit"
 | |
| 
 | |
| #  define SESSION_MANAGER_TARGET "org.gnome.SessionManager"
 | |
| #  define SESSION_MANAGER_OBJECT "/org/gnome/SessionManager"
 | |
| #  define SESSION_MANAGER_INTERFACE "org.gnome.SessionManager"
 | |
| 
 | |
| #  define DBUS_TIMEOUT (-1)
 | |
| #endif
 | |
| 
 | |
| using namespace mozilla;
 | |
| using namespace mozilla::widget;
 | |
| 
 | |
| NS_IMPL_ISUPPORTS(WakeLockListener, nsIDOMMozWakeLockListener)
 | |
| 
 | |
| #define WAKE_LOCK_LOG(str, ...)                        \
 | |
|   MOZ_LOG(gLinuxWakeLockLog, mozilla::LogLevel::Debug, \
 | |
|           ("[%p] " str, this, ##__VA_ARGS__))
 | |
| static mozilla::LazyLogModule gLinuxWakeLockLog("LinuxWakeLock");
 | |
| 
 | |
| enum WakeLockType {
 | |
|   Initial = 0,
 | |
| #if defined(MOZ_ENABLE_DBUS)
 | |
|   FreeDesktopScreensaver = 1,
 | |
|   FreeDesktopPower = 2,
 | |
|   FreeDesktopPortal = 3,
 | |
|   GNOME = 4,
 | |
| #endif
 | |
| #if defined(MOZ_X11)
 | |
|   XScreenSaver = 5,
 | |
| #endif
 | |
| #if defined(MOZ_WAYLAND)
 | |
|   WaylandIdleInhibit = 6,
 | |
| #endif
 | |
|   Unsupported = 7,
 | |
| };
 | |
| 
 | |
| #if defined(MOZ_ENABLE_DBUS)
 | |
| bool IsDBusWakeLock(int aWakeLockType) {
 | |
|   switch (aWakeLockType) {
 | |
|     case FreeDesktopScreensaver:
 | |
|     case FreeDesktopPower:
 | |
|     case GNOME:
 | |
|     case FreeDesktopPortal:
 | |
|       return true;
 | |
|     default:
 | |
|       return false;
 | |
|   }
 | |
| }
 | |
| #endif
 | |
| 
 | |
| #ifdef MOZ_LOGGING
 | |
| const char* WakeLockTypeNames[] = {
 | |
|     "Initial",
 | |
|     "FreeDesktopScreensaver",
 | |
|     "FreeDesktopPower",
 | |
|     "FreeDesktopPortal",
 | |
|     "GNOME",
 | |
|     "XScreenSaver",
 | |
|     "WaylandIdleInhibit",
 | |
|     "Unsupported",
 | |
| };
 | |
| #endif
 | |
| 
 | |
| class WakeLockTopic {
 | |
|  public:
 | |
|   NS_INLINE_DECL_REFCOUNTING(WakeLockTopic)
 | |
| 
 | |
|   explicit WakeLockTopic(const nsAString& aTopic) {
 | |
|     CopyUTF16toUTF8(aTopic, mTopic);
 | |
|     WAKE_LOCK_LOG("WakeLockTopic::WakeLockTopic() created %s", mTopic.get());
 | |
|     if (sWakeLockType == Initial) {
 | |
|       SwitchToNextWakeLockType();
 | |
|     }
 | |
| #ifdef MOZ_ENABLE_DBUS
 | |
|     mCancellable = dont_AddRef(g_cancellable_new());
 | |
| #endif
 | |
|   }
 | |
| 
 | |
|   nsresult InhibitScreensaver();
 | |
|   nsresult UninhibitScreensaver();
 | |
| 
 | |
|   void Shutdown();
 | |
| 
 | |
|  private:
 | |
|   bool SendInhibit();
 | |
|   bool SendUninhibit();
 | |
| 
 | |
| #if defined(MOZ_X11)
 | |
|   bool CheckXScreenSaverSupport();
 | |
|   bool InhibitXScreenSaver(bool inhibit);
 | |
| #endif
 | |
| 
 | |
| #if defined(MOZ_WAYLAND)
 | |
|   zwp_idle_inhibitor_v1* mWaylandInhibitor = nullptr;
 | |
|   static bool CheckWaylandIdleInhibitSupport();
 | |
|   bool InhibitWaylandIdle();
 | |
|   bool UninhibitWaylandIdle();
 | |
| #endif
 | |
| 
 | |
|   bool IsNativeWakeLock(int aWakeLockType);
 | |
|   bool IsWakeLockTypeAvailable(int aWakeLockType);
 | |
|   bool SwitchToNextWakeLockType();
 | |
| 
 | |
| #ifdef MOZ_ENABLE_DBUS
 | |
|   void DBusInhibitScreensaver(const char* aName, const char* aPath,
 | |
|                               const char* aCall, const char* aMethod,
 | |
|                               RefPtr<GVariant> aArgs);
 | |
|   void DBusUninhibitScreensaver(const char* aName, const char* aPath,
 | |
|                                 const char* aCall, const char* aMethod);
 | |
| 
 | |
|   void InhibitFreeDesktopPortal();
 | |
|   void InhibitFreeDesktopScreensaver();
 | |
|   void InhibitFreeDesktopPower();
 | |
|   void InhibitGNOME();
 | |
| 
 | |
|   void UninhibitFreeDesktopPortal();
 | |
|   void UninhibitFreeDesktopScreensaver();
 | |
|   void UninhibitFreeDesktopPower();
 | |
|   void UninhibitGNOME();
 | |
| 
 | |
|   void DBusInhibitSucceeded(uint32_t aInhibitRequestID);
 | |
|   void DBusInhibitFailed(bool aFatal);
 | |
|   void DBusUninhibitSucceeded();
 | |
|   void DBusUninhibitFailed();
 | |
|   void ClearDBusInhibitToken();
 | |
| #endif
 | |
|   ~WakeLockTopic() = default;
 | |
| 
 | |
|   // Why is screensaver inhibited
 | |
|   nsCString mTopic;
 | |
| 
 | |
|   // Our desired state
 | |
|   bool mShouldInhibit = false;
 | |
| 
 | |
|   // Our actual sate
 | |
|   bool mInhibited = false;
 | |
| 
 | |
| #ifdef MOZ_ENABLE_DBUS
 | |
|   // We're waiting for DBus reply (inhibit/uninhibit calls).
 | |
|   bool mWaitingForDBusInhibit = false;
 | |
|   bool mWaitingForDBusUninhibit = false;
 | |
| 
 | |
|   // mInhibitRequestID is received from success screen saver inhibit call
 | |
|   // and it's needed for screen saver enablement.
 | |
|   Maybe<uint32_t> mInhibitRequestID;
 | |
| 
 | |
|   RefPtr<GCancellable> mCancellable;
 | |
|   // Used to uninhibit org.freedesktop.portal.Inhibit request
 | |
|   nsCString mRequestObjectPath;
 | |
| #endif
 | |
| 
 | |
|   static int sWakeLockType;
 | |
| };
 | |
| 
 | |
| int WakeLockTopic::sWakeLockType = Initial;
 | |
| 
 | |
| #ifdef MOZ_ENABLE_DBUS
 | |
| void WakeLockTopic::DBusInhibitSucceeded(uint32_t aInhibitRequestID) {
 | |
|   mWaitingForDBusInhibit = false;
 | |
|   mInhibitRequestID = Some(aInhibitRequestID);
 | |
|   mInhibited = true;
 | |
| 
 | |
|   WAKE_LOCK_LOG(
 | |
|       "WakeLockTopic::DBusInhibitSucceeded(), mInhibitRequestID %u "
 | |
|       "mShouldInhibit %d",
 | |
|       *mInhibitRequestID, mShouldInhibit);
 | |
| 
 | |
|   // Uninhibit was requested before inhibit request was finished.
 | |
|   // So ask for it now.
 | |
|   if (!mShouldInhibit) {
 | |
|     UninhibitScreensaver();
 | |
|   }
 | |
| }
 | |
| 
 | |
| void WakeLockTopic::DBusInhibitFailed(bool aFatal) {
 | |
|   WAKE_LOCK_LOG("WakeLockTopic::DBusInhibitFailed(%d)", aFatal);
 | |
| 
 | |
|   mWaitingForDBusInhibit = false;
 | |
|   ClearDBusInhibitToken();
 | |
| 
 | |
|   // Non-recoverable DBus error. Switch to another wake lock type.
 | |
|   if (aFatal && SwitchToNextWakeLockType()) {
 | |
|     SendInhibit();
 | |
|   }
 | |
| }
 | |
| 
 | |
| void WakeLockTopic::DBusUninhibitSucceeded() {
 | |
|   WAKE_LOCK_LOG("WakeLockTopic::DBusUninhibitSucceeded() mShouldInhibit %d",
 | |
|                 mShouldInhibit);
 | |
| 
 | |
|   mWaitingForDBusUninhibit = false;
 | |
|   mInhibited = false;
 | |
|   ClearDBusInhibitToken();
 | |
| 
 | |
|   // Inhibit was requested before uninhibit request was finished.
 | |
|   // So ask for it now.
 | |
|   if (mShouldInhibit) {
 | |
|     InhibitScreensaver();
 | |
|   }
 | |
| }
 | |
| 
 | |
| void WakeLockTopic::DBusUninhibitFailed() {
 | |
|   WAKE_LOCK_LOG("WakeLockTopic::DBusUninhibitFailed()");
 | |
|   mWaitingForDBusUninhibit = false;
 | |
|   mInhibitRequestID = Nothing();
 | |
| }
 | |
| 
 | |
| void WakeLockTopic::ClearDBusInhibitToken() {
 | |
|   mRequestObjectPath.Truncate();
 | |
|   mInhibitRequestID = Nothing();
 | |
| }
 | |
| 
 | |
| void WakeLockTopic::DBusInhibitScreensaver(const char* aName, const char* aPath,
 | |
|                                            const char* aCall,
 | |
|                                            const char* aMethod,
 | |
|                                            RefPtr<GVariant> aArgs) {
 | |
|   WAKE_LOCK_LOG(
 | |
|       "WakeLockTopic::DBusInhibitScreensaver() mWaitingForDBusInhibit %d "
 | |
|       "mWaitingForDBusUninhibit %d",
 | |
|       mWaitingForDBusInhibit, mWaitingForDBusUninhibit);
 | |
|   if (mWaitingForDBusInhibit) {
 | |
|     WAKE_LOCK_LOG("  already waiting to inihibit, return");
 | |
|     return;
 | |
|   }
 | |
|   if (mWaitingForDBusUninhibit) {
 | |
|     WAKE_LOCK_LOG("  cancel un-inihibit request");
 | |
|     g_cancellable_cancel(mCancellable);
 | |
|     mWaitingForDBusUninhibit = false;
 | |
|   }
 | |
|   mWaitingForDBusInhibit = true;
 | |
| 
 | |
|   widget::CreateDBusProxyForBus(
 | |
|       G_BUS_TYPE_SESSION,
 | |
|       GDBusProxyFlags(G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS |
 | |
|                       G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES),
 | |
|       /* aInterfaceInfo = */ nullptr, aName, aPath, aCall, mCancellable)
 | |
|       ->Then(
 | |
|           GetCurrentSerialEventTarget(), __func__,
 | |
|           [self = RefPtr{this}, this, args = RefPtr{aArgs},
 | |
|            aMethod](RefPtr<GDBusProxy>&& aProxy) {
 | |
|             WAKE_LOCK_LOG(
 | |
|                 "WakeLockTopic::DBusInhibitScreensaver() proxy created");
 | |
|             DBusProxyCall(aProxy.get(), aMethod, args.get(),
 | |
|                           G_DBUS_CALL_FLAGS_NONE, DBUS_TIMEOUT, mCancellable)
 | |
|                 ->Then(
 | |
|                     GetCurrentSerialEventTarget(), __func__,
 | |
|                     [s = RefPtr{this}, this](RefPtr<GVariant>&& aResult) {
 | |
|                       if (!g_variant_is_of_type(aResult.get(),
 | |
|                                                 G_VARIANT_TYPE_TUPLE) ||
 | |
|                           g_variant_n_children(aResult.get()) != 1) {
 | |
|                         WAKE_LOCK_LOG(
 | |
|                             "WakeLockTopic::DBusInhibitScreensaver() wrong "
 | |
|                             "reply type %s\n",
 | |
|                             g_variant_get_type_string(aResult.get()));
 | |
|                         DBusInhibitFailed(/* aFatal */ true);
 | |
|                         return;
 | |
|                       }
 | |
|                       RefPtr<GVariant> variant = dont_AddRef(
 | |
|                           g_variant_get_child_value(aResult.get(), 0));
 | |
|                       if (!g_variant_is_of_type(variant,
 | |
|                                                 G_VARIANT_TYPE_UINT32)) {
 | |
|                         WAKE_LOCK_LOG(
 | |
|                             "WakeLockTopic::DBusInhibitScreensaver() wrong "
 | |
|                             "reply type %s\n",
 | |
|                             g_variant_get_type_string(aResult.get()));
 | |
|                         DBusInhibitFailed(/* aFatal */ true);
 | |
|                         return;
 | |
|                       }
 | |
|                       DBusInhibitSucceeded(g_variant_get_uint32(variant));
 | |
|                     },
 | |
|                     [s = RefPtr{this}, this,
 | |
|                      aMethod](GUniquePtr<GError>&& aError) {
 | |
|                       // Failed to send inhibit request over proxy.
 | |
|                       // Switch to another wake lock type.
 | |
|                       WAKE_LOCK_LOG(
 | |
|                           "WakeLockTopic::DBusInhibitFailed() %s call failed : "
 | |
|                           "%s\n",
 | |
|                           aMethod, aError->message);
 | |
|                       DBusInhibitFailed(
 | |
|                           /* aFatal */ !IsCancelledGError(aError.get()));
 | |
|                     });
 | |
|           },
 | |
|           [self = RefPtr{this}, this](GUniquePtr<GError>&& aError) {
 | |
|             // We failed to create DBus proxy. Switch to another
 | |
|             // wake lock type.
 | |
|             WAKE_LOCK_LOG(
 | |
|                 "WakeLockTopic::DBusInhibitScreensaver() Proxy creation "
 | |
|                 "failed: %s\n",
 | |
|                 aError->message);
 | |
|             DBusInhibitFailed(/* aFatal */ !IsCancelledGError(aError.get()));
 | |
|           });
 | |
| }
 | |
| 
 | |
| void WakeLockTopic::DBusUninhibitScreensaver(const char* aName,
 | |
|                                              const char* aPath,
 | |
|                                              const char* aCall,
 | |
|                                              const char* aMethod) {
 | |
|   WAKE_LOCK_LOG(
 | |
|       "WakeLockTopic::DBusUninhibitScreensaver() mWaitingForDBusInhibit %d "
 | |
|       "mWaitingForDBusUninhibit %d request id %d",
 | |
|       mWaitingForDBusInhibit, mWaitingForDBusUninhibit,
 | |
|       mInhibitRequestID ? *mInhibitRequestID : -1);
 | |
| 
 | |
|   if (mWaitingForDBusUninhibit) {
 | |
|     WAKE_LOCK_LOG("  already waiting to uninihibit, return");
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   if (mWaitingForDBusInhibit) {
 | |
|     WAKE_LOCK_LOG("  cancel inihibit request");
 | |
|     g_cancellable_cancel(mCancellable);
 | |
|     mWaitingForDBusInhibit = false;
 | |
|   }
 | |
| 
 | |
|   if (!mInhibitRequestID.isSome()) {
 | |
|     WAKE_LOCK_LOG("  missing inihibit token, quit.");
 | |
|     // missing uninhibit token, just quit.
 | |
|     return;
 | |
|   }
 | |
|   mWaitingForDBusUninhibit = true;
 | |
| 
 | |
|   RefPtr<GVariant> variant =
 | |
|       dont_AddRef(g_variant_ref_sink(g_variant_new("(u)", *mInhibitRequestID)));
 | |
|   nsCOMPtr<nsISerialEventTarget> target = GetCurrentSerialEventTarget();
 | |
|   widget::CreateDBusProxyForBus(
 | |
|       G_BUS_TYPE_SESSION,
 | |
|       GDBusProxyFlags(G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS |
 | |
|                       G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES),
 | |
|       /* aInterfaceInfo = */ nullptr, aName, aPath, aCall, mCancellable)
 | |
|       ->Then(
 | |
|           target, __func__,
 | |
|           [self = RefPtr{this}, this, args = std::move(variant), target,
 | |
|            aMethod](RefPtr<GDBusProxy>&& aProxy) {
 | |
|             WAKE_LOCK_LOG(
 | |
|                 "WakeLockTopic::DBusUninhibitScreensaver() proxy created");
 | |
|             DBusProxyCall(aProxy.get(), aMethod, args.get(),
 | |
|                           G_DBUS_CALL_FLAGS_NONE, DBUS_TIMEOUT, mCancellable)
 | |
|                 ->Then(
 | |
|                     target, __func__,
 | |
|                     [s = RefPtr{this}, this](RefPtr<GVariant>&& aResult) {
 | |
|                       DBusUninhibitSucceeded();
 | |
|                     },
 | |
|                     [s = RefPtr{this}, this,
 | |
|                      aMethod](GUniquePtr<GError>&& aError) {
 | |
|                       WAKE_LOCK_LOG(
 | |
|                           "WakeLockTopic::DBusUninhibitFailed() %s call failed "
 | |
|                           ": %s\n",
 | |
|                           aMethod, aError->message);
 | |
|                       DBusUninhibitFailed();
 | |
|                     });
 | |
|           },
 | |
|           [self = RefPtr{this}, this](GUniquePtr<GError>&& aError) {
 | |
|             WAKE_LOCK_LOG(
 | |
|                 "WakeLockTopic::DBusUninhibitFailed() Proxy creation failed: "
 | |
|                 "%s\n",
 | |
|                 aError->message);
 | |
|             DBusUninhibitFailed();
 | |
|           });
 | |
| }
 | |
| 
 | |
| void WakeLockTopic::InhibitFreeDesktopPortal() {
 | |
|   WAKE_LOCK_LOG(
 | |
|       "WakeLockTopic::InhibitFreeDesktopPortal() mWaitingForDBusInhibit %d "
 | |
|       "mWaitingForDBusUninhibit %d",
 | |
|       mWaitingForDBusInhibit, mWaitingForDBusUninhibit);
 | |
|   if (mWaitingForDBusInhibit) {
 | |
|     WAKE_LOCK_LOG("  already waiting to inihibit, return");
 | |
|     return;
 | |
|   }
 | |
|   if (mWaitingForDBusUninhibit) {
 | |
|     WAKE_LOCK_LOG("  cancel un-inihibit request");
 | |
|     g_cancellable_cancel(mCancellable);
 | |
|     mWaitingForDBusUninhibit = false;
 | |
|   }
 | |
|   mWaitingForDBusInhibit = true;
 | |
| 
 | |
|   CreateDBusProxyForBus(
 | |
|       G_BUS_TYPE_SESSION,
 | |
|       GDBusProxyFlags(G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS |
 | |
|                       G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES),
 | |
|       nullptr, FREEDESKTOP_PORTAL_DESKTOP_TARGET,
 | |
|       FREEDESKTOP_PORTAL_DESKTOP_OBJECT, FREEDESKTOP_PORTAL_DESKTOP_INTERFACE,
 | |
|       mCancellable)
 | |
|       ->Then(
 | |
|           GetCurrentSerialEventTarget(), __func__,
 | |
|           [self = RefPtr{this}, this](RefPtr<GDBusProxy>&& aProxy) {
 | |
|             GVariantBuilder b;
 | |
|             g_variant_builder_init(&b, G_VARIANT_TYPE_VARDICT);
 | |
|             g_variant_builder_add(&b, "{sv}", "reason",
 | |
|                                   g_variant_new_string(self->mTopic.get()));
 | |
| 
 | |
|             // From
 | |
|             // https://flatpak.github.io/xdg-desktop-portal/docs/#gdbus-org.freedesktop.portal.Inhibit
 | |
|             DBusProxyCall(
 | |
|                 aProxy.get(), "Inhibit",
 | |
|                 g_variant_new("(sua{sv})", g_get_prgname(),
 | |
|                               FREEDESKTOP_PORTAL_DESKTOP_INHIBIT_IDLE_FLAG, &b),
 | |
|                 G_DBUS_CALL_FLAGS_NONE, DBUS_TIMEOUT, mCancellable)
 | |
|                 ->Then(
 | |
|                     GetCurrentSerialEventTarget(), __func__,
 | |
|                     [s = RefPtr{this}, this](RefPtr<GVariant>&& aResult) {
 | |
|                       gchar* requestObjectPath = nullptr;
 | |
|                       g_variant_get(aResult, "(o)", &requestObjectPath);
 | |
|                       if (!requestObjectPath) {
 | |
|                         WAKE_LOCK_LOG(
 | |
|                             "WakeLockTopic::InhibitFreeDesktopPortal(): Unable "
 | |
|                             "to get requestObjectPath\n");
 | |
|                         DBusInhibitFailed(/* aFatal */ true);
 | |
|                         return;
 | |
|                       }
 | |
|                       WAKE_LOCK_LOG(
 | |
|                           "WakeLockTopic::InhibitFreeDesktopPortal(): "
 | |
|                           "inhibited, objpath to unihibit: %s\n",
 | |
|                           requestObjectPath);
 | |
|                       mRequestObjectPath.Adopt(requestObjectPath);
 | |
|                       DBusInhibitSucceeded(0);
 | |
|                     },
 | |
|                     [s = RefPtr{this}, this](GUniquePtr<GError>&& aError) {
 | |
|                       DBusInhibitFailed(
 | |
|                           /* aFatal */ !IsCancelledGError(aError.get()));
 | |
|                       WAKE_LOCK_LOG(
 | |
|                           "Failed to create DBus proxy for "
 | |
|                           "org.freedesktop.portal.Desktop: %s\n",
 | |
|                           aError->message);
 | |
|                     });
 | |
|           },
 | |
|           [self = RefPtr{this}, this](GUniquePtr<GError>&& aError) {
 | |
|             WAKE_LOCK_LOG(
 | |
|                 "Failed to create DBus proxy for "
 | |
|                 "org.freedesktop.portal.Desktop: %s\n",
 | |
|                 aError->message);
 | |
|             DBusInhibitFailed(/* aFatal */ !IsCancelledGError(aError.get()));
 | |
|           });
 | |
| }
 | |
| 
 | |
| void WakeLockTopic::InhibitFreeDesktopScreensaver() {
 | |
|   WAKE_LOCK_LOG("InhibitFreeDesktopScreensaver()");
 | |
|   DBusInhibitScreensaver(FREEDESKTOP_SCREENSAVER_TARGET,
 | |
|                          FREEDESKTOP_SCREENSAVER_OBJECT,
 | |
|                          FREEDESKTOP_SCREENSAVER_INTERFACE, "Inhibit",
 | |
|                          dont_AddRef(g_variant_ref_sink(g_variant_new(
 | |
|                              "(ss)", g_get_prgname(), mTopic.get()))));
 | |
| }
 | |
| 
 | |
| void WakeLockTopic::InhibitFreeDesktopPower() {
 | |
|   WAKE_LOCK_LOG("InhibitFreeDesktopPower()");
 | |
|   DBusInhibitScreensaver(FREEDESKTOP_POWER_TARGET, FREEDESKTOP_POWER_OBJECT,
 | |
|                          FREEDESKTOP_POWER_INTERFACE, "Inhibit",
 | |
|                          dont_AddRef(g_variant_ref_sink(g_variant_new(
 | |
|                              "(ss)", g_get_prgname(), mTopic.get()))));
 | |
| }
 | |
| 
 | |
| void WakeLockTopic::InhibitGNOME() {
 | |
|   WAKE_LOCK_LOG("InhibitGNOME()");
 | |
|   static const uint32_t xid = 0;
 | |
|   static const uint32_t flags = (1 << 3);  // Inhibit idle
 | |
|   DBusInhibitScreensaver(
 | |
|       SESSION_MANAGER_TARGET, SESSION_MANAGER_OBJECT, SESSION_MANAGER_INTERFACE,
 | |
|       "Inhibit",
 | |
|       dont_AddRef(g_variant_ref_sink(
 | |
|           g_variant_new("(susu)", g_get_prgname(), xid, mTopic.get(), flags))));
 | |
| }
 | |
| 
 | |
| void WakeLockTopic::UninhibitFreeDesktopPortal() {
 | |
|   WAKE_LOCK_LOG(
 | |
|       "WakeLockTopic::UninhibitFreeDesktopPortal() mWaitingForDBusInhibit %d "
 | |
|       "mWaitingForDBusUninhibit %d object path: %s",
 | |
|       mWaitingForDBusInhibit, mWaitingForDBusUninhibit,
 | |
|       mRequestObjectPath.get());
 | |
| 
 | |
|   if (mWaitingForDBusUninhibit) {
 | |
|     WAKE_LOCK_LOG("  already waiting to uninihibit, return");
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   if (mWaitingForDBusInhibit) {
 | |
|     WAKE_LOCK_LOG("  cancel inihibit request");
 | |
|     g_cancellable_cancel(mCancellable);
 | |
|     mWaitingForDBusInhibit = false;
 | |
|   }
 | |
|   if (mRequestObjectPath.IsEmpty()) {
 | |
|     WAKE_LOCK_LOG("UninhibitFreeDesktopPortal() failed: unknown object path\n");
 | |
|     return;
 | |
|   }
 | |
|   mWaitingForDBusUninhibit = true;
 | |
| 
 | |
|   nsCOMPtr<nsISerialEventTarget> target = GetCurrentSerialEventTarget();
 | |
|   CreateDBusProxyForBus(
 | |
|       G_BUS_TYPE_SESSION,
 | |
|       GDBusProxyFlags(G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS |
 | |
|                       G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES),
 | |
|       nullptr, FREEDESKTOP_PORTAL_DESKTOP_TARGET, mRequestObjectPath.get(),
 | |
|       "org.freedesktop.portal.Request", mCancellable)
 | |
|       ->Then(
 | |
|           target, __func__,
 | |
|           [self = RefPtr{this}, target, this](RefPtr<GDBusProxy>&& aProxy) {
 | |
|             DBusProxyCall(aProxy.get(), "Close", nullptr,
 | |
|                           G_DBUS_CALL_FLAGS_NONE, DBUS_TIMEOUT, mCancellable)
 | |
|                 ->Then(
 | |
|                     target, __func__,
 | |
|                     [s = RefPtr{this}, this](RefPtr<GVariant>&& aResult) {
 | |
|                       DBusUninhibitSucceeded();
 | |
|                       WAKE_LOCK_LOG(
 | |
|                           "WakeLockTopic::UninhibitFreeDesktopPortal() Inhibit "
 | |
|                           "removed\n");
 | |
|                     },
 | |
|                     [s = RefPtr{this}, this](GUniquePtr<GError>&& aError) {
 | |
|                       DBusUninhibitFailed();
 | |
|                       WAKE_LOCK_LOG(
 | |
|                           "WakeLockTopic::UninhibitFreeDesktopPortal() "
 | |
|                           "Removing inhibit failed: %s\n",
 | |
|                           aError->message);
 | |
|                     });
 | |
|           },
 | |
|           [self = RefPtr{this}, this](GUniquePtr<GError>&& aError) {
 | |
|             WAKE_LOCK_LOG(
 | |
|                 "WakeLockTopic::UninhibitFreeDesktopPortal() Proxy creation "
 | |
|                 "failed: %s\n",
 | |
|                 aError->message);
 | |
|             DBusUninhibitFailed();
 | |
|           });
 | |
| }
 | |
| 
 | |
| void WakeLockTopic::UninhibitFreeDesktopScreensaver() {
 | |
|   WAKE_LOCK_LOG("UninhibitFreeDesktopScreensaver()");
 | |
|   DBusUninhibitScreensaver(FREEDESKTOP_SCREENSAVER_TARGET,
 | |
|                            FREEDESKTOP_SCREENSAVER_OBJECT,
 | |
|                            FREEDESKTOP_SCREENSAVER_INTERFACE, "UnInhibit");
 | |
| }
 | |
| 
 | |
| void WakeLockTopic::UninhibitFreeDesktopPower() {
 | |
|   WAKE_LOCK_LOG("UninhibitFreeDesktopPower()");
 | |
|   DBusUninhibitScreensaver(FREEDESKTOP_POWER_TARGET, FREEDESKTOP_POWER_OBJECT,
 | |
|                            FREEDESKTOP_POWER_INTERFACE, "UnInhibit");
 | |
| }
 | |
| 
 | |
| void WakeLockTopic::UninhibitGNOME() {
 | |
|   WAKE_LOCK_LOG("UninhibitGNOME()");
 | |
|   DBusUninhibitScreensaver(SESSION_MANAGER_TARGET, SESSION_MANAGER_OBJECT,
 | |
|                            SESSION_MANAGER_INTERFACE, "Uninhibit");
 | |
| }
 | |
| #endif
 | |
| 
 | |
| #if defined(MOZ_X11)
 | |
| // TODO: Merge with Idle service?
 | |
| typedef Bool (*_XScreenSaverQueryExtension_fn)(Display* dpy, int* event_base,
 | |
|                                                int* error_base);
 | |
| typedef Bool (*_XScreenSaverQueryVersion_fn)(Display* dpy, int* major,
 | |
|                                              int* minor);
 | |
| typedef void (*_XScreenSaverSuspend_fn)(Display* dpy, Bool suspend);
 | |
| 
 | |
| static PRLibrary* sXssLib = nullptr;
 | |
| static _XScreenSaverQueryExtension_fn _XSSQueryExtension = nullptr;
 | |
| static _XScreenSaverQueryVersion_fn _XSSQueryVersion = nullptr;
 | |
| static _XScreenSaverSuspend_fn _XSSSuspend = nullptr;
 | |
| 
 | |
| /* static */
 | |
| bool WakeLockTopic::CheckXScreenSaverSupport() {
 | |
|   if (!sXssLib) {
 | |
|     sXssLib = PR_LoadLibrary("libXss.so.1");
 | |
|     if (!sXssLib) {
 | |
|       return false;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   _XSSQueryExtension = (_XScreenSaverQueryExtension_fn)PR_FindFunctionSymbol(
 | |
|       sXssLib, "XScreenSaverQueryExtension");
 | |
|   _XSSQueryVersion = (_XScreenSaverQueryVersion_fn)PR_FindFunctionSymbol(
 | |
|       sXssLib, "XScreenSaverQueryVersion");
 | |
|   _XSSSuspend = (_XScreenSaverSuspend_fn)PR_FindFunctionSymbol(
 | |
|       sXssLib, "XScreenSaverSuspend");
 | |
|   if (!_XSSQueryExtension || !_XSSQueryVersion || !_XSSSuspend) {
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   GdkDisplay* gDisplay = gdk_display_get_default();
 | |
|   if (!GdkIsX11Display(gDisplay)) {
 | |
|     return false;
 | |
|   }
 | |
|   Display* display = GDK_DISPLAY_XDISPLAY(gDisplay);
 | |
| 
 | |
|   int throwaway;
 | |
|   if (!_XSSQueryExtension(display, &throwaway, &throwaway)) return false;
 | |
| 
 | |
|   int major, minor;
 | |
|   if (!_XSSQueryVersion(display, &major, &minor)) return false;
 | |
|   // Needs to be compatible with version 1.1
 | |
|   if (major != 1) return false;
 | |
|   if (minor < 1) return false;
 | |
| 
 | |
|   WAKE_LOCK_LOG("XScreenSaver supported.");
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| /* static */
 | |
| bool WakeLockTopic::InhibitXScreenSaver(bool inhibit) {
 | |
|   WAKE_LOCK_LOG("InhibitXScreenSaver %d", inhibit);
 | |
| 
 | |
|   // Should only be called if CheckXScreenSaverSupport returns true.
 | |
|   // There's a couple of safety checks here nonetheless.
 | |
|   if (!_XSSSuspend) {
 | |
|     return false;
 | |
|   }
 | |
|   GdkDisplay* gDisplay = gdk_display_get_default();
 | |
|   if (!GdkIsX11Display(gDisplay)) {
 | |
|     return false;
 | |
|   }
 | |
|   Display* display = GDK_DISPLAY_XDISPLAY(gDisplay);
 | |
|   _XSSSuspend(display, inhibit);
 | |
| 
 | |
|   WAKE_LOCK_LOG("InhibitXScreenSaver %d succeeded", inhibit);
 | |
|   mInhibited = inhibit;
 | |
|   return true;
 | |
| }
 | |
| #endif
 | |
| 
 | |
| #if defined(MOZ_WAYLAND)
 | |
| /* static */
 | |
| bool WakeLockTopic::CheckWaylandIdleInhibitSupport() {
 | |
|   nsWaylandDisplay* waylandDisplay = WaylandDisplayGet();
 | |
|   return waylandDisplay && waylandDisplay->GetIdleInhibitManager() != nullptr;
 | |
| }
 | |
| 
 | |
| bool WakeLockTopic::InhibitWaylandIdle() {
 | |
|   WAKE_LOCK_LOG("InhibitWaylandIdle()");
 | |
| 
 | |
|   nsWaylandDisplay* waylandDisplay = WaylandDisplayGet();
 | |
|   if (!waylandDisplay) {
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   nsWindow* focusedWindow = nsWindow::GetFocusedWindow();
 | |
|   if (!focusedWindow) {
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   UninhibitWaylandIdle();
 | |
| 
 | |
|   MozContainerSurfaceLock lock(focusedWindow->GetMozContainer());
 | |
|   struct wl_surface* waylandSurface = lock.GetSurface();
 | |
|   if (waylandSurface) {
 | |
|     mWaylandInhibitor = zwp_idle_inhibit_manager_v1_create_inhibitor(
 | |
|         waylandDisplay->GetIdleInhibitManager(), waylandSurface);
 | |
|     mInhibited = true;
 | |
|   }
 | |
| 
 | |
|   WAKE_LOCK_LOG("InhibitWaylandIdle() %s",
 | |
|                 !!mWaylandInhibitor ? "succeeded" : "failed");
 | |
|   return !!mWaylandInhibitor;
 | |
| }
 | |
| 
 | |
| bool WakeLockTopic::UninhibitWaylandIdle() {
 | |
|   WAKE_LOCK_LOG("UninhibitWaylandIdle() mWaylandInhibitor %p",
 | |
|                 mWaylandInhibitor);
 | |
| 
 | |
|   mInhibited = false;
 | |
|   if (!mWaylandInhibitor) {
 | |
|     return false;
 | |
|   }
 | |
|   zwp_idle_inhibitor_v1_destroy(mWaylandInhibitor);
 | |
|   mWaylandInhibitor = nullptr;
 | |
|   return true;
 | |
| }
 | |
| #endif
 | |
| 
 | |
| bool WakeLockTopic::SendInhibit() {
 | |
|   WAKE_LOCK_LOG("WakeLockTopic::SendInhibit() WakeLockType %s",
 | |
|                 WakeLockTypeNames[sWakeLockType]);
 | |
|   MOZ_ASSERT(sWakeLockType != Initial);
 | |
| 
 | |
|   switch (sWakeLockType) {
 | |
| #if defined(MOZ_ENABLE_DBUS)
 | |
|     case FreeDesktopPortal:
 | |
|       InhibitFreeDesktopPortal();
 | |
|       break;
 | |
|     case FreeDesktopScreensaver:
 | |
|       InhibitFreeDesktopScreensaver();
 | |
|       break;
 | |
|     case FreeDesktopPower:
 | |
|       InhibitFreeDesktopPower();
 | |
|       break;
 | |
|     case GNOME:
 | |
|       InhibitGNOME();
 | |
|       break;
 | |
| #endif
 | |
| #if defined(MOZ_X11)
 | |
|     case XScreenSaver:
 | |
|       return InhibitXScreenSaver(true);
 | |
| #endif
 | |
| #if defined(MOZ_WAYLAND)
 | |
|     case WaylandIdleInhibit:
 | |
|       return InhibitWaylandIdle();
 | |
| #endif
 | |
|     default:
 | |
|       return false;
 | |
|   }
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| bool WakeLockTopic::SendUninhibit() {
 | |
|   WAKE_LOCK_LOG("WakeLockTopic::SendUninhibit() WakeLockType %s",
 | |
|                 WakeLockTypeNames[sWakeLockType]);
 | |
|   MOZ_ASSERT(sWakeLockType != Initial);
 | |
|   switch (sWakeLockType) {
 | |
| #if defined(MOZ_ENABLE_DBUS)
 | |
|     case FreeDesktopPortal:
 | |
|       UninhibitFreeDesktopPortal();
 | |
|       break;
 | |
|     case FreeDesktopScreensaver:
 | |
|       UninhibitFreeDesktopScreensaver();
 | |
|       break;
 | |
|     case FreeDesktopPower:
 | |
|       UninhibitFreeDesktopPower();
 | |
|       break;
 | |
|     case GNOME:
 | |
|       UninhibitGNOME();
 | |
|       break;
 | |
| #endif
 | |
| #if defined(MOZ_X11)
 | |
|     case XScreenSaver:
 | |
|       return InhibitXScreenSaver(false);
 | |
| #endif
 | |
| #if defined(MOZ_WAYLAND)
 | |
|     case WaylandIdleInhibit:
 | |
|       return UninhibitWaylandIdle();
 | |
| #endif
 | |
|     default:
 | |
|       return false;
 | |
|   }
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| nsresult WakeLockTopic::InhibitScreensaver() {
 | |
|   WAKE_LOCK_LOG("WakeLockTopic::InhibitScreensaver() Inhibited %d", mInhibited);
 | |
| 
 | |
|   if (mInhibited) {
 | |
|     // Screensaver is inhibited. Nothing to do here.
 | |
|     return NS_OK;
 | |
|   }
 | |
|   mShouldInhibit = true;
 | |
| 
 | |
|   // Iterate through wake lock types in case of failure.
 | |
|   while (!SendInhibit()) {
 | |
|     // We don't switch away from native locks. Just try again.
 | |
|     if (IsNativeWakeLock(sWakeLockType)) {
 | |
|       return NS_ERROR_FAILURE;
 | |
|     }
 | |
|     if (!SwitchToNextWakeLockType()) {
 | |
|       return NS_ERROR_FAILURE;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return (sWakeLockType != Unsupported) ? NS_OK : NS_ERROR_FAILURE;
 | |
| }
 | |
| 
 | |
| void WakeLockTopic::Shutdown() {
 | |
|   WAKE_LOCK_LOG("WakeLockTopic::Shutdown() state %d", mInhibited);
 | |
| #ifdef MOZ_ENABLE_DBUS
 | |
|   if (mWaitingForDBusUninhibit) {
 | |
|     return;
 | |
|   }
 | |
|   g_cancellable_cancel(mCancellable);
 | |
| #endif
 | |
|   if (mInhibited) {
 | |
|     UninhibitScreensaver();
 | |
|   }
 | |
| }
 | |
| 
 | |
| nsresult WakeLockTopic::UninhibitScreensaver() {
 | |
|   WAKE_LOCK_LOG("WakeLockTopic::UninhibitScreensaver() Inhibited %d",
 | |
|                 mInhibited);
 | |
| 
 | |
|   if (!mInhibited) {
 | |
|     // Screensaver isn't inhibited. Nothing to do here.
 | |
|     return NS_OK;
 | |
|   }
 | |
|   mShouldInhibit = false;
 | |
| 
 | |
|   // Don't switch wake lock type in case of failure.
 | |
|   // We need to use the same lock/unlock type.
 | |
|   return SendUninhibit() ? NS_OK : NS_ERROR_FAILURE;
 | |
| }
 | |
| 
 | |
| bool WakeLockTopic::IsWakeLockTypeAvailable(int aWakeLockType) {
 | |
|   switch (aWakeLockType) {
 | |
| #if defined(MOZ_ENABLE_DBUS)
 | |
|     case FreeDesktopPortal:
 | |
|     case FreeDesktopScreensaver:
 | |
|     case FreeDesktopPower:
 | |
|     case GNOME:
 | |
|       return true;
 | |
| #endif
 | |
| #if defined(MOZ_X11)
 | |
|     case XScreenSaver:
 | |
|       if (!GdkIsX11Display()) {
 | |
|         return false;
 | |
|       }
 | |
|       if (!CheckXScreenSaverSupport()) {
 | |
|         WAKE_LOCK_LOG("  XScreenSaverSupport is missing!");
 | |
|         return false;
 | |
|       }
 | |
|       return true;
 | |
| #endif
 | |
| #if defined(MOZ_WAYLAND)
 | |
|     case WaylandIdleInhibit:
 | |
|       if (!GdkIsWaylandDisplay()) {
 | |
|         return false;
 | |
|       }
 | |
|       if (!CheckWaylandIdleInhibitSupport()) {
 | |
|         WAKE_LOCK_LOG("  WaylandIdleInhibitSupport is missing!");
 | |
|         return false;
 | |
|       }
 | |
|       return true;
 | |
| #endif
 | |
|     default:
 | |
|       return false;
 | |
|   }
 | |
| }
 | |
| 
 | |
| bool WakeLockTopic::IsNativeWakeLock(int aWakeLockType) {
 | |
|   switch (aWakeLockType) {
 | |
| #if defined(MOZ_X11)
 | |
|     case XScreenSaver:
 | |
|       return true;
 | |
| #endif
 | |
| #if defined(MOZ_WAYLAND)
 | |
|     case WaylandIdleInhibit:
 | |
|       return true;
 | |
| #endif
 | |
|     default:
 | |
|       return false;
 | |
|   }
 | |
| }
 | |
| 
 | |
| bool WakeLockTopic::SwitchToNextWakeLockType() {
 | |
|   WAKE_LOCK_LOG("WakeLockTopic::SwitchToNextWakeLockType() WakeLockType %s",
 | |
|                 WakeLockTypeNames[sWakeLockType]);
 | |
| 
 | |
|   if (sWakeLockType == Unsupported) {
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
| #ifdef MOZ_LOGGING
 | |
|   auto printWakeLocktype = MakeScopeExit([&] {
 | |
|     WAKE_LOCK_LOG("  switched to WakeLockType %s",
 | |
|                   WakeLockTypeNames[sWakeLockType]);
 | |
|   });
 | |
| #endif
 | |
| 
 | |
| #if defined(MOZ_ENABLE_DBUS)
 | |
|   if (IsDBusWakeLock(sWakeLockType)) {
 | |
|     // We're switching out of DBus wakelock - clear our recent DBus states.
 | |
|     mWaitingForDBusInhibit = false;
 | |
|     mWaitingForDBusUninhibit = false;
 | |
|     mInhibited = false;
 | |
|     ClearDBusInhibitToken();
 | |
|   }
 | |
| #endif
 | |
| 
 | |
|   while (sWakeLockType != Unsupported) {
 | |
|     sWakeLockType++;
 | |
|     if (IsWakeLockTypeAvailable(sWakeLockType)) {
 | |
|       return true;
 | |
|     }
 | |
|   }
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| WakeLockListener::WakeLockListener() = default;
 | |
| 
 | |
| WakeLockListener::~WakeLockListener() {
 | |
|   for (const auto& topic : mTopics.Values()) {
 | |
|     topic->Shutdown();
 | |
|   }
 | |
| }
 | |
| 
 | |
| nsresult WakeLockListener::Callback(const nsAString& topic,
 | |
|                                     const nsAString& state) {
 | |
|   if (!topic.Equals(u"screen"_ns) && !topic.Equals(u"video-playing"_ns) &&
 | |
|       !topic.Equals(u"autoscroll"_ns)) {
 | |
|     return NS_OK;
 | |
|   }
 | |
| 
 | |
|   RefPtr<WakeLockTopic> topicLock = mTopics.LookupOrInsertWith(
 | |
|       topic, [&] { return MakeRefPtr<WakeLockTopic>(topic); });
 | |
| 
 | |
|   // Treat "locked-background" the same as "unlocked" on desktop linux.
 | |
|   bool shouldLock = state.EqualsLiteral("locked-foreground");
 | |
|   WAKE_LOCK_LOG("WakeLockListener topic %s state %s request lock %d",
 | |
|                 NS_ConvertUTF16toUTF8(topic).get(),
 | |
|                 NS_ConvertUTF16toUTF8(state).get(), shouldLock);
 | |
| 
 | |
|   return shouldLock ? topicLock->InhibitScreensaver()
 | |
|                     : topicLock->UninhibitScreensaver();
 | |
| }
 |