1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
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/. */
5 
6 #ifndef GMPServiceParent_h_
7 #define GMPServiceParent_h_
8 
9 #include "GMPService.h"
10 #include "mozilla/gmp/PGMPServiceParent.h"
11 #include "mozIGeckoMediaPluginChromeService.h"
12 #include "nsClassHashtable.h"
13 #include "nsTHashMap.h"
14 #include "mozilla/Atomics.h"
15 #include "nsNetUtil.h"
16 #include "nsIAsyncShutdown.h"
17 #include "nsRefPtrHashtable.h"
18 #include "nsThreadUtils.h"
19 #include "mozilla/MozPromise.h"
20 #include "GMPStorage.h"
21 
22 template <class>
23 struct already_AddRefed;
24 
25 namespace mozilla {
26 class OriginAttributesPattern;
27 
28 namespace gmp {
29 
30 class GMPParent;
31 class GMPServiceParent;
32 
33 class GeckoMediaPluginServiceParent final
34     : public GeckoMediaPluginService,
35       public mozIGeckoMediaPluginChromeService,
36       public nsIAsyncShutdownBlocker {
37  public:
38   static already_AddRefed<GeckoMediaPluginServiceParent> GetSingleton();
39 
40   GeckoMediaPluginServiceParent();
41   nsresult Init() override;
42 
43   NS_DECL_ISUPPORTS_INHERITED
44   NS_DECL_NSIASYNCSHUTDOWNBLOCKER
45 
46   // mozIGeckoMediaPluginService
47   NS_IMETHOD HasPluginForAPI(const nsACString& aAPI, nsTArray<nsCString>* aTags,
48                              bool* aRetVal) override;
49   NS_IMETHOD GetNodeId(const nsAString& aOrigin,
50                        const nsAString& aTopLevelOrigin,
51                        const nsAString& aGMPName,
52                        UniquePtr<GetNodeIdCallback>&& aCallback) override;
53 
54   NS_DECL_MOZIGECKOMEDIAPLUGINCHROMESERVICE
55   NS_DECL_NSIOBSERVER
56 
57   RefPtr<GenericPromise> EnsureInitialized();
58   RefPtr<GenericPromise> AsyncAddPluginDirectory(const nsAString& aDirectory);
59 
60   // GMP thread access only
61   bool IsShuttingDown();
62 
63   already_AddRefed<GMPStorage> GetMemoryStorageFor(const nsACString& aNodeId);
64   nsresult ForgetThisSiteNative(
65       const nsAString& aSite, const mozilla::OriginAttributesPattern& aPattern);
66 
67   nsresult ForgetThisBaseDomainNative(const nsAString& aBaseDomain);
68 
69   // Notifies that some user of this class is created/destroyed.
70   void ServiceUserCreated(GMPServiceParent* aServiceParent);
71   void ServiceUserDestroyed(GMPServiceParent* aServiceParent);
72 
73   void UpdateContentProcessGMPCapabilities();
74 
75  private:
76   friend class GMPServiceParent;
77 
78   virtual ~GeckoMediaPluginServiceParent();
79 
80   void ClearStorage();
81 
82   already_AddRefed<GMPParent> SelectPluginForAPI(
83       const nsACString& aNodeId, const nsCString& aAPI,
84       const nsTArray<nsCString>& aTags);
85 
86   already_AddRefed<GMPParent> FindPluginForAPIFrom(
87       size_t aSearchStartIndex, const nsCString& aAPI,
88       const nsTArray<nsCString>& aTags, size_t* aOutPluginIndex);
89 
90   nsresult GetNodeId(const nsAString& aOrigin, const nsAString& aTopLevelOrigin,
91                      const nsAString& aGMPName, nsACString& aOutId);
92 
93   void UnloadPlugins();
94   void CrashPlugins();
95   void NotifySyncShutdownComplete();
96 
97   void RemoveOnGMPThread(const nsAString& aDirectory,
98                          const bool aDeleteFromDisk, const bool aCanDefer);
99 
100   struct DirectoryFilter {
101     virtual bool operator()(nsIFile* aPath) = 0;
102     ~DirectoryFilter() = default;
103   };
104   void ClearNodeIdAndPlugin(DirectoryFilter& aFilter);
105   void ClearNodeIdAndPlugin(nsIFile* aPluginStorageDir,
106                             DirectoryFilter& aFilter);
107   void ForgetThisSiteOnGMPThread(
108       const nsACString& aSite,
109       const mozilla::OriginAttributesPattern& aPattern);
110   void ForgetThisBaseDomainOnGMPThread(const nsACString& aBaseDomain);
111   void ClearRecentHistoryOnGMPThread(PRTime aSince);
112 
113   already_AddRefed<GMPParent> GetById(uint32_t aPluginId);
114 
115  protected:
116   friend class GMPParent;
117   void ReAddOnGMPThread(const RefPtr<GMPParent>& aOld);
118   void PluginTerminated(const RefPtr<GMPParent>& aOld);
119   void InitializePlugins(nsISerialEventTarget* GMPThread) override;
120   RefPtr<GenericPromise> LoadFromEnvironment();
121   RefPtr<GenericPromise> AddOnGMPThread(nsString aDirectory);
122 
123   RefPtr<GetGMPContentParentPromise> GetContentParent(
124       GMPCrashHelper* aHelper, const NodeIdVariant& aNodeIdVariant,
125       const nsCString& aAPI, const nsTArray<nsCString>& aTags) override;
126 
127  private:
128   // Creates a copy of aOriginal. Note that the caller is responsible for
129   // adding this to GeckoMediaPluginServiceParent::mPlugins.
130   already_AddRefed<GMPParent> ClonePlugin(const GMPParent* aOriginal);
131   nsresult EnsurePluginsOnDiskScanned();
132   nsresult InitStorage();
133 
134   // Get a string based node ID from a NodeIdVariant. This will
135   // either fetch the internal string, or convert the internal NodeIdParts to a
136   // string. The conversion process is fallible, so the return value should be
137   // checked.
138   nsresult GetNodeId(const NodeIdVariant& aNodeIdVariant, nsACString& aOutId);
139 
140   class PathRunnable : public Runnable {
141    public:
142     enum EOperation {
143       REMOVE,
144       REMOVE_AND_DELETE_FROM_DISK,
145     };
146 
147     PathRunnable(GeckoMediaPluginServiceParent* aService,
148                  const nsAString& aPath, EOperation aOperation,
149                  bool aDefer = false)
150         : Runnable("gmp::GeckoMediaPluginServiceParent::PathRunnable"),
151           mService(aService),
152           mPath(aPath),
153           mOperation(aOperation),
154           mDefer(aDefer) {}
155 
156     NS_DECL_NSIRUNNABLE
157 
158    private:
159     RefPtr<GeckoMediaPluginServiceParent> mService;
160     nsString mPath;
161     EOperation mOperation;
162     bool mDefer;
163   };
164 
165   // Protected by mMutex from the base class.
166   nsTArray<RefPtr<GMPParent>> mPlugins;
167   bool mShuttingDown;
168 
169   // True if we've inspected MOZ_GMP_PATH on the GMP thread and loaded any
170   // plugins found there into mPlugins.
171   Atomic<bool> mScannedPluginOnDisk;
172 
173   template <typename T>
174   class MainThreadOnly {
175    public:
MainThreadOnly(T aValue)176     MOZ_IMPLICIT MainThreadOnly(T aValue) : mValue(aValue) {}
177     operator T&() {
178       MOZ_ASSERT(NS_IsMainThread());
179       return mValue;
180     }
181 
182    private:
183     T mValue;
184   };
185 
186   MainThreadOnly<bool> mWaitingForPluginsSyncShutdown;
187 
188   nsTArray<nsString> mPluginsWaitingForDeletion;
189 
190   nsCOMPtr<nsIFile> mStorageBaseDir;
191 
192   // Hashes of (origin,topLevelOrigin) to the node id for
193   // non-persistent sessions.
194   nsClassHashtable<nsUint32HashKey, nsCString> mTempNodeIds;
195 
196   // Hashes node id to whether that node id is allowed to store data
197   // persistently on disk.
198   nsTHashMap<nsCStringHashKey, bool> mPersistentStorageAllowed;
199 
200   // Synchronization for barrier that ensures we've loaded GMPs from
201   // MOZ_GMP_PATH before allowing GetContentParentFrom() to proceed.
202   Monitor mInitPromiseMonitor;
203   MozMonitoredPromiseHolder<GenericPromise> mInitPromise;
204   bool mLoadPluginsFromDiskComplete;
205 
206   // Hashes nodeId to the hashtable of storage for that nodeId.
207   nsRefPtrHashtable<nsCStringHashKey, GMPStorage> mTempGMPStorage;
208 
209   // Tracks how many IPC connections to GMPServices running in content
210   // processes we have. When this is empty we can safely shut down.
211   // Synchronized across thread via mMutex in base class.
212   nsTArray<GMPServiceParent*> mServiceParents;
213 };
214 
215 nsresult WriteToFile(nsIFile* aPath, const nsCString& aFileName,
216                      const nsCString& aData);
217 nsresult ReadSalt(nsIFile* aPath, nsACString& aOutData);
218 bool MatchOrigin(nsIFile* aPath, const nsACString& aSite,
219                  const mozilla::OriginAttributesPattern& aPattern);
220 bool MatchBaseDomain(nsIFile* aPath, const nsACString& aBaseDomain);
221 
222 class GMPServiceParent final : public PGMPServiceParent {
223  public:
224   explicit GMPServiceParent(GeckoMediaPluginServiceParent* aService);
225 
226   // Our refcounting is thread safe, and when our refcount drops to zero
227   // we dispatch an event to the main thread to delete the GMPServiceParent.
228   // Note that this means it's safe for references to this object to be
229   // released on a non main thread, but the destructor will always run on
230   // the main thread.
231 
232   // Mark AddRef and Release as `final`, as they overload pure virtual
233   // implementations in PGMPServiceParent.
234   NS_INLINE_DECL_THREADSAFE_REFCOUNTING_WITH_DELETE_ON_MAIN_THREAD(
235       GMPServiceParent, final);
236 
237   ipc::IPCResult RecvGetGMPNodeId(const nsString& aOrigin,
238                                   const nsString& aTopLevelOrigin,
239                                   const nsString& aGMPName,
240                                   nsCString* aID) override;
241 
242   static bool Create(Endpoint<PGMPServiceParent>&& aGMPService);
243 
244   ipc::IPCResult RecvLaunchGMP(
245       const NodeIdVariant& aNodeIdVariant, const nsCString& aAPI,
246       nsTArray<nsCString>&& aTags, nsTArray<ProcessId>&& aAlreadyBridgedTo,
247       uint32_t* aOutPluginId, ProcessId* aOutProcessId,
248       nsCString* aOutDisplayName, Endpoint<PGMPContentParent>* aOutEndpoint,
249       nsresult* aOutRv, nsCString* aOutErrorDescription) override;
250 
251  private:
252   ~GMPServiceParent();
253 
254   RefPtr<GeckoMediaPluginServiceParent> mService;
255 };
256 
257 }  // namespace gmp
258 }  // namespace mozilla
259 
260 #endif  // GMPServiceParent_h_
261