1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2  *
3  * This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 #ifndef WIDGET_GTK_MPRIS_SERVICE_HANDLER_H_
8 #define WIDGET_GTK_MPRIS_SERVICE_HANDLER_H_
9 
10 #include <gio/gio.h>
11 #include "mozilla/dom/FetchImageHelper.h"
12 #include "mozilla/dom/MediaControlKeySource.h"
13 #include "mozilla/Attributes.h"
14 #include "mozilla/UniquePtr.h"
15 #include "nsIFile.h"
16 #include "nsMimeTypes.h"
17 #include "nsString.h"
18 
19 #define DBUS_MPRIS_SERVICE_NAME "org.mpris.MediaPlayer2.firefox"
20 #define DBUS_MPRIS_OBJECT_PATH "/org/mpris/MediaPlayer2"
21 #define DBUS_MPRIS_INTERFACE "org.mpris.MediaPlayer2"
22 #define DBUS_MPRIS_PLAYER_INTERFACE "org.mpris.MediaPlayer2.Player"
23 #define DBUS_MPRIS_TRACK_PATH "/org/mpris/MediaPlayer2/firefox"
24 
25 namespace mozilla {
26 namespace widget {
27 
28 /**
29  * This class implements the "MPRIS" D-Bus Service
30  * (https://specifications.freedesktop.org/mpris-spec/2.2),
31  * which is used to communicate with the Desktop Environment about the
32  * Multimedia playing in Gecko.
33  * Note that this interface requires many methods which may not be supported by
34  * Gecko, the interface
35  * however provides CanXYZ properties for these methods, so the method is
36  * defined but won't be executed.
37  *
38  * Also note that the following defines are for parts that the MPRIS Spec
39  * defines optional. The code won't
40  * compile with any of the defines set, yet, as those aren't implemented yet and
41  * probably never will be of
42  * use for gecko. For sake of completeness, they have been added until the
43  * decision about their implementation
44  * is finally made.
45  *
46  * The constexpr'ed methods are capabilities of the user agent known at compile
47  * time, e.g. we decided at
48  * compile time whether we ever want to support closing the user agent via MPRIS
49  * (Quit() and CanQuit()).
50  *
51  * Other properties like CanPlay() might depend on the runtime state (is there
52  * media available for playback?)
53  * and thus aren't a constexpr but merely a const method.
54  */
55 class MPRISServiceHandler final : public dom::MediaControlKeySource {
NS_INLINE_DECL_REFCOUNTING(MPRISServiceHandler,override)56   NS_INLINE_DECL_REFCOUNTING(MPRISServiceHandler, override)
57  public:
58   // Note that this constructor does NOT initialize the MPRIS Service but only
59   // this class. The method Open() is responsible for registering and MAY FAIL.
60 
61   // The image format used in MPRIS is based on the mMimeType here. Although
62   // IMAGE_JPEG or IMAGE_BMP are valid types as well but a png image with
63   // transparent background will be converted into a jpeg/bmp file with a
64   // colored background IMAGE_PNG format seems to be the best choice for now.
65   MPRISServiceHandler() : mMimeType(IMAGE_PNG){};
66   bool Open() override;
67   void Close() override;
68   bool IsOpened() const override;
69 
70   // From the EventSource.
71   void SetPlaybackState(dom::MediaSessionPlaybackState aState) override;
72 
73   // GetPlaybackState returns dom::PlaybackState. GetPlaybackStatus returns this
74   // state converted into d-bus variants.
75   GVariant* GetPlaybackStatus() const;
76 
77   const char* Identity() const;
78   const char* DesktopEntry() const;
79   bool PressKey(dom::MediaControlKey aKey) const;
80 
81   void SetMediaMetadata(const dom::MediaMetadataBase& aMetadata) override;
82   GVariant* GetMetadataAsGVariant() const;
83 
84   void SetSupportedMediaKeys(const MediaKeysArray& aSupportedKeys) override;
85 
86   bool IsMediaKeySupported(dom::MediaControlKey aKey) const;
87 
88  private:
89   ~MPRISServiceHandler();
90 
91   // Note: Registration Ids for the D-Bus start with 1, so a value of 0
92   // indicates an error (or an object which wasn't initialized yet)
93 
94   // a handle to our bus registration/ownership
95   guint mOwnerId = 0;
96   // This is for the interface org.mpris.MediaPlayer2
97   guint mRootRegistrationId = 0;
98   // This is for the interface org.mpris.MediaPlayer2.Player
99   guint mPlayerRegistrationId = 0;
100   GDBusNodeInfo* mIntrospectionData = nullptr;
101   GDBusConnection* mConnection = nullptr;
102   bool mInitialized = false;
103   nsAutoCString mIdentity;
104   nsAutoCString mDesktopEntry;
105 
106   nsCString mMimeType;
107 
108   // A bitmask indicating what keys are enabled
109   uint32_t mSupportedKeys = 0;
110 
111   class MPRISMetadata : public dom::MediaMetadataBase {
112    public:
113     MPRISMetadata() = default;
114     ~MPRISMetadata() = default;
115 
UpdateFromMetadataBase(const dom::MediaMetadataBase & aMetadata)116     void UpdateFromMetadataBase(const dom::MediaMetadataBase& aMetadata) {
117       mTitle = aMetadata.mTitle;
118       mArtist = aMetadata.mArtist;
119       mAlbum = aMetadata.mAlbum;
120       mArtwork = aMetadata.mArtwork;
121     }
Clear()122     void Clear() {
123       UpdateFromMetadataBase(MediaMetadataBase::EmptyData());
124       mArtUrl.Truncate();
125     }
126 
127     nsCString mArtUrl;
128   };
129   MPRISMetadata mMPRISMetadata;
130 
131   // The saved image file fetched from the URL
132   nsCOMPtr<nsIFile> mLocalImageFile;
133   nsCOMPtr<nsIFile> mLocalImageFolder;
134 
135   mozilla::UniquePtr<mozilla::dom::FetchImageHelper> mImageFetcher;
136   mozilla::MozPromiseRequestHolder<mozilla::dom::ImagePromise>
137       mImageFetchRequest;
138 
139   nsString mFetchingUrl;
140   nsString mCurrentImageUrl;
141 
142   size_t mNextImageIndex = 0;
143 
144   // Load the image at index aIndex of the metadta's artwork to MPRIS
145   // asynchronously
146   void LoadImageAtIndex(const size_t aIndex);
147   bool SetImageToDisplay(const char* aImageData, uint32_t aDataSize);
148 
149   bool RenewLocalImageFile(const char* aImageData, uint32_t aDataSize);
150   bool InitLocalImageFile();
151   bool InitLocalImageFolder();
152   void RemoveAllLocalImages();
153   bool LocalImageFolderExists();
154 
155   // Queries nsAppInfo to get the branded browser name and vendor
156   void InitIdentity();
157 
158   // non-public API, called from events
159   void OnNameAcquired(GDBusConnection* aConnection, const gchar* aName);
160   void OnNameLost(GDBusConnection* aConnection, const gchar* aName);
161   void OnBusAcquired(GDBusConnection* aConnection, const gchar* aName);
162 
163   static void OnNameAcquiredStatic(GDBusConnection* aConnection,
164                                    const gchar* aName, gpointer aUserData);
165   static void OnNameLostStatic(GDBusConnection* aConnection, const gchar* aName,
166                                gpointer aUserData);
167   static void OnBusAcquiredStatic(GDBusConnection* aConnection,
168                                   const gchar* aName, gpointer aUserData);
169 
170   void EmitEvent(dom::MediaControlKey aKey) const;
171 
172   bool EmitMetadataChanged() const;
173 
174   void SetMediaMetadataInternal(const dom::MediaMetadataBase& aMetadata,
175                                 bool aClearArtUrl = true);
176 
177   bool EmitSupportedKeyChanged(mozilla::dom::MediaControlKey aKey,
178                                bool aSupported) const;
179 
180   bool EmitPropertiesChangedSignal(GVariant* aParameters) const;
181 
182   void ClearMetadata();
183 };
184 
185 }  // namespace widget
186 }  // namespace mozilla
187 
188 #endif  // WIDGET_GTK_MPRIS_SERVICE_HANDLER_H_
189