LCOV - code coverage report
Current view: top level - widget/gtk - WakeLockListener.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 13 131 9.9 %
Date: 2017-07-14 16:53:18 Functions: 5 19 26.3 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* vim:expandtab:shiftwidth=2:tabstop=2:
       3             :  */
       4             : /* This Source Code Form is subject to the terms of the Mozilla Public
       5             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       6             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       7             : 
       8             : #ifdef MOZ_ENABLE_DBUS
       9             : 
      10             : #include "WakeLockListener.h"
      11             : 
      12             : #include <dbus/dbus.h>
      13             : #include <dbus/dbus-glib-lowlevel.h>
      14             : 
      15             : #include "mozilla/ipc/DBusMessageRefPtr.h"
      16             : #include "mozilla/ipc/DBusPendingCallRefPtr.h"
      17             : 
      18             : #define FREEDESKTOP_SCREENSAVER_TARGET    "org.freedesktop.ScreenSaver"
      19             : #define FREEDESKTOP_SCREENSAVER_OBJECT    "/ScreenSaver"
      20             : #define FREEDESKTOP_SCREENSAVER_INTERFACE "org.freedesktop.ScreenSaver"
      21             : 
      22             : #define SESSION_MANAGER_TARGET            "org.gnome.SessionManager"
      23             : #define SESSION_MANAGER_OBJECT            "/org/gnome/SessionManager"
      24             : #define SESSION_MANAGER_INTERFACE         "org.gnome.SessionManager"
      25             : 
      26             : #define DBUS_TIMEOUT                      (-1)
      27             : 
      28             : using namespace mozilla;
      29             : 
      30           5 : NS_IMPL_ISUPPORTS(WakeLockListener, nsIDOMMozWakeLockListener)
      31             : 
      32           3 : StaticRefPtr<WakeLockListener> WakeLockListener::sSingleton;
      33             : 
      34             : 
      35             : enum DesktopEnvironment {
      36             :   FreeDesktop,
      37             :   GNOME,
      38             :   Unsupported,
      39             : };
      40             : 
      41           0 : class WakeLockTopic
      42             : {
      43             : public:
      44           0 :   WakeLockTopic(const nsAString& aTopic, DBusConnection* aConnection)
      45           0 :     : mTopic(NS_ConvertUTF16toUTF8(aTopic))
      46             :     , mConnection(aConnection)
      47             :     , mDesktopEnvironment(FreeDesktop)
      48             :     , mInhibitRequest(0)
      49             :     , mShouldInhibit(false)
      50           0 :     , mWaitingForReply(false)
      51             :   {
      52           0 :   }
      53             : 
      54             :   nsresult InhibitScreensaver(void);
      55             :   nsresult UninhibitScreensaver(void);
      56             : 
      57             : private:
      58             :   bool SendInhibit();
      59             :   bool SendUninhibit();
      60             : 
      61             :   bool SendFreeDesktopInhibitMessage();
      62             :   bool SendGNOMEInhibitMessage();
      63             :   bool SendMessage(DBusMessage* aMessage);
      64             : 
      65             :   static void ReceiveInhibitReply(DBusPendingCall* aPending, void* aUserData);
      66             :   void InhibitFailed();
      67             :   void InhibitSucceeded(uint32_t aInhibitRequest);
      68             : 
      69             :   nsCString mTopic;
      70             :   RefPtr<DBusConnection> mConnection;
      71             : 
      72             :   DesktopEnvironment mDesktopEnvironment;
      73             : 
      74             :   uint32_t mInhibitRequest;
      75             : 
      76             :   bool mShouldInhibit;
      77             :   bool mWaitingForReply;
      78             : };
      79             : 
      80             : 
      81             : bool
      82           0 : WakeLockTopic::SendMessage(DBusMessage* aMessage)
      83             : {
      84             :   // send message and get a handle for a reply
      85           0 :   RefPtr<DBusPendingCall> reply;
      86           0 :   dbus_connection_send_with_reply(mConnection, aMessage,
      87             :                                   reply.StartAssignment(),
      88           0 :                                   DBUS_TIMEOUT);
      89           0 :   if (!reply) {
      90           0 :     return false;
      91             :   }
      92             : 
      93           0 :   dbus_pending_call_set_notify(reply, &ReceiveInhibitReply, this, NULL);
      94             : 
      95           0 :   return true;
      96             : }
      97             : 
      98             : bool
      99           0 : WakeLockTopic::SendFreeDesktopInhibitMessage()
     100             : {
     101           0 :   RefPtr<DBusMessage> message = already_AddRefed<DBusMessage>(
     102             :     dbus_message_new_method_call(FREEDESKTOP_SCREENSAVER_TARGET,
     103             :                                  FREEDESKTOP_SCREENSAVER_OBJECT,
     104             :                                  FREEDESKTOP_SCREENSAVER_INTERFACE,
     105           0 :                                  "Inhibit"));
     106             : 
     107           0 :   if (!message) {
     108           0 :     return false;
     109             :   }
     110             : 
     111           0 :   const char* app = g_get_prgname();
     112           0 :   const char* topic = mTopic.get();
     113           0 :   dbus_message_append_args(message,
     114             :                            DBUS_TYPE_STRING, &app,
     115             :                            DBUS_TYPE_STRING, &topic,
     116           0 :                            DBUS_TYPE_INVALID);
     117             : 
     118           0 :   return SendMessage(message);
     119             : }
     120             : 
     121             : bool
     122           0 : WakeLockTopic::SendGNOMEInhibitMessage()
     123             : {
     124           0 :   RefPtr<DBusMessage> message = already_AddRefed<DBusMessage>(
     125             :     dbus_message_new_method_call(SESSION_MANAGER_TARGET,
     126             :                                  SESSION_MANAGER_OBJECT,
     127             :                                  SESSION_MANAGER_INTERFACE,
     128           0 :                                  "Inhibit"));
     129             : 
     130           0 :   if (!message) {
     131           0 :     return false;
     132             :   }
     133             : 
     134             :   static const uint32_t xid = 0;
     135             :   static const uint32_t flags = (1 << 3); // Inhibit idle
     136           0 :   const char* app = g_get_prgname();
     137           0 :   const char* topic = mTopic.get();
     138           0 :   dbus_message_append_args(message,
     139             :                            DBUS_TYPE_STRING, &app,
     140             :                            DBUS_TYPE_UINT32, &xid,
     141             :                            DBUS_TYPE_STRING, &topic,
     142             :                            DBUS_TYPE_UINT32, &flags,
     143           0 :                            DBUS_TYPE_INVALID);
     144             : 
     145           0 :   return SendMessage(message);
     146             : }
     147             : 
     148             : 
     149             : bool
     150           0 : WakeLockTopic::SendInhibit()
     151             : {
     152           0 :   bool sendOk = false;
     153             : 
     154           0 :   switch (mDesktopEnvironment)
     155             :   {
     156             :   case FreeDesktop:
     157           0 :     sendOk = SendFreeDesktopInhibitMessage();
     158           0 :     break;
     159             :   case GNOME:
     160           0 :     sendOk = SendGNOMEInhibitMessage();
     161           0 :     break;
     162             :   case Unsupported:
     163           0 :     return false;
     164             :   }
     165             : 
     166           0 :   if (sendOk) {
     167           0 :     mWaitingForReply = true;
     168             :   }
     169             : 
     170           0 :   return sendOk;
     171             : }
     172             : 
     173             : bool
     174           0 : WakeLockTopic::SendUninhibit()
     175             : {
     176           0 :   RefPtr<DBusMessage> message;
     177             : 
     178           0 :   if (mDesktopEnvironment == FreeDesktop) {
     179           0 :     message = already_AddRefed<DBusMessage>(
     180             :       dbus_message_new_method_call(FREEDESKTOP_SCREENSAVER_TARGET,
     181             :                                    FREEDESKTOP_SCREENSAVER_OBJECT,
     182             :                                    FREEDESKTOP_SCREENSAVER_INTERFACE,
     183           0 :                                    "UnInhibit"));
     184           0 :   } else if (mDesktopEnvironment == GNOME) {
     185           0 :     message = already_AddRefed<DBusMessage>(
     186             :       dbus_message_new_method_call(SESSION_MANAGER_TARGET,
     187             :                                    SESSION_MANAGER_OBJECT,
     188             :                                    SESSION_MANAGER_INTERFACE,
     189           0 :                                    "Uninhibit"));
     190             :   }
     191             : 
     192           0 :   if (!message) {
     193           0 :     return false;
     194             :   }
     195             : 
     196           0 :   dbus_message_append_args(message,
     197             :                            DBUS_TYPE_UINT32, &mInhibitRequest,
     198           0 :                            DBUS_TYPE_INVALID);
     199             : 
     200           0 :   dbus_connection_send(mConnection, message, nullptr);
     201           0 :   dbus_connection_flush(mConnection);
     202             : 
     203           0 :   mInhibitRequest = 0;
     204             : 
     205           0 :   return true;
     206             : }
     207             : 
     208             : nsresult
     209           0 : WakeLockTopic::InhibitScreensaver()
     210             : {
     211           0 :   if (mShouldInhibit) {
     212             :     // Screensaver is inhibited. Nothing to do here.
     213           0 :     return NS_OK;
     214             :   }
     215             : 
     216           0 :   mShouldInhibit = true;
     217             : 
     218           0 :   if (mWaitingForReply) {
     219             :     // We already have a screensaver inhibit request pending. This can happen
     220             :     // if InhibitScreensaver is called, then UninhibitScreensaver, then
     221             :     // InhibitScreensaver again quickly.
     222           0 :     return NS_OK;
     223             :   }
     224             : 
     225           0 :   return SendInhibit() ? NS_OK : NS_ERROR_FAILURE;
     226             : }
     227             : 
     228             : nsresult
     229           0 : WakeLockTopic::UninhibitScreensaver()
     230             : {
     231           0 :   if (!mShouldInhibit) {
     232             :     // Screensaver isn't inhibited. Nothing to do here.
     233           0 :     return NS_OK;
     234             :   }
     235             : 
     236           0 :   mShouldInhibit = false;
     237             : 
     238           0 :   if (mWaitingForReply) {
     239             :     // If we're still waiting for a response to our inhibit request, we can't
     240             :     // do anything until we get a dbus message back. The callbacks below will
     241             :     // check |mShouldInhibit| and act accordingly.
     242           0 :     return NS_OK;
     243             :   }
     244             : 
     245           0 :   return SendUninhibit() ? NS_OK : NS_ERROR_FAILURE;
     246             : }
     247             : 
     248             : void
     249           0 : WakeLockTopic::InhibitFailed()
     250             : {
     251           0 :   mWaitingForReply = false;
     252             : 
     253           0 :   if (mDesktopEnvironment == FreeDesktop) {
     254           0 :     mDesktopEnvironment = GNOME;
     255             :   } else {
     256           0 :     NS_ASSERTION(mDesktopEnvironment == GNOME, "Unknown desktop environment");
     257           0 :     mDesktopEnvironment = Unsupported;
     258           0 :     mShouldInhibit = false;
     259             :   }
     260             : 
     261           0 :   if (!mShouldInhibit) {
     262             :     // We were interrupted by UninhibitScreensaver() before we could find the
     263             :     // correct desktop environment.
     264           0 :     return;
     265             :   }
     266             : 
     267           0 :   SendInhibit();
     268             : }
     269             : 
     270             : void
     271           0 : WakeLockTopic::InhibitSucceeded(uint32_t aInhibitRequest)
     272             : {
     273           0 :   mWaitingForReply = false;
     274           0 :   mInhibitRequest = aInhibitRequest;
     275             : 
     276           0 :   if (!mShouldInhibit) {
     277             :     // We successfully inhibited the screensaver, but UninhibitScreensaver()
     278             :     // was called while we were waiting for a reply.
     279           0 :     SendUninhibit();
     280             :   }
     281           0 : }
     282             : 
     283             : /* static */ void
     284           0 : WakeLockTopic::ReceiveInhibitReply(DBusPendingCall* pending, void* user_data)
     285             : {
     286           0 :   if (!WakeLockListener::GetSingleton(false)) {
     287             :     // The WakeLockListener (and therefore our topic) was deleted while we were
     288             :     // waiting for a reply.
     289           0 :     return;
     290             :   }
     291             : 
     292           0 :   WakeLockTopic* self = static_cast<WakeLockTopic*>(user_data);
     293             : 
     294           0 :   RefPtr<DBusMessage> msg = already_AddRefed<DBusMessage>(
     295           0 :     dbus_pending_call_steal_reply(pending));
     296           0 :   if (!msg) {
     297           0 :     return;
     298             :   }
     299             : 
     300           0 :   if (dbus_message_get_type(msg) == DBUS_MESSAGE_TYPE_METHOD_RETURN) {
     301             :     uint32_t inhibitRequest;
     302             : 
     303           0 :     if (dbus_message_get_args(msg, nullptr, DBUS_TYPE_UINT32,
     304             :                               &inhibitRequest, DBUS_TYPE_INVALID)) {
     305           0 :       self->InhibitSucceeded(inhibitRequest);
     306             :     }
     307             :   } else {
     308           0 :     self->InhibitFailed();
     309             :   }
     310             : }
     311             : 
     312             : 
     313           1 : WakeLockListener::WakeLockListener()
     314           2 :   : mConnection(already_AddRefed<DBusConnection>(
     315           3 :     dbus_bus_get(DBUS_BUS_SESSION, nullptr)))
     316             : {
     317           1 :   if (mConnection) {
     318           1 :     dbus_connection_set_exit_on_disconnect(mConnection, false);
     319           1 :     dbus_connection_setup_with_g_main(mConnection, nullptr);
     320             :   }
     321           1 : }
     322             : 
     323             : /* static */ WakeLockListener*
     324           1 : WakeLockListener::GetSingleton(bool aCreate)
     325             : {
     326           1 :   if (!sSingleton && aCreate) {
     327           1 :     sSingleton = new WakeLockListener();
     328             :   }
     329             : 
     330           1 :   return sSingleton;
     331             : }
     332             : 
     333             : /* static */ void
     334           0 : WakeLockListener::Shutdown()
     335             : {
     336           0 :   sSingleton = nullptr;
     337           0 : }
     338             : 
     339             : nsresult
     340           0 : WakeLockListener::Callback(const nsAString& topic, const nsAString& state)
     341             : {
     342           0 :   if (!mConnection) {
     343           0 :     return NS_ERROR_FAILURE;
     344             :   }
     345             : 
     346           0 :   if(!topic.Equals(NS_LITERAL_STRING("screen")))
     347           0 :     return NS_OK;
     348             : 
     349           0 :   WakeLockTopic* topicLock = mTopics.Get(topic);
     350           0 :   if (!topicLock) {
     351           0 :     topicLock = new WakeLockTopic(topic, mConnection);
     352           0 :     mTopics.Put(topic, topicLock);
     353             :   }
     354             : 
     355             :   // Treat "locked-background" the same as "unlocked" on desktop linux.
     356           0 :   bool shouldLock = state.EqualsLiteral("locked-foreground");
     357             : 
     358           0 :   return shouldLock ?
     359           0 :     topicLock->InhibitScreensaver() :
     360           0 :     topicLock->UninhibitScreensaver();
     361             : }
     362             : 
     363             : #endif

Generated by: LCOV version 1.13