Tue, 06 Jan 2015 21:39:09 +0100
Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim:set ts=2 sw=2 sts=2 et cindent: */
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 file,
5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "mozilla/Preferences.h"
7 #include "mozilla/dom/TimeRanges.h"
8 #include "MediaResource.h"
9 #include "mozilla/dom/HTMLMediaElement.h"
10 #include "MediaPluginHost.h"
11 #include "nsXPCOMStrings.h"
12 #include "nsISeekableStream.h"
13 #include "MediaPluginReader.h"
14 #include "nsIGfxInfo.h"
15 #include "gfxCrashReporterUtils.h"
16 #include "prmem.h"
17 #include "prlink.h"
18 #include "MediaResourceServer.h"
19 #include "nsServiceManagerUtils.h"
21 #include "MPAPI.h"
23 #include "nsIPropertyBag2.h"
25 #if defined(ANDROID) || defined(MOZ_WIDGET_GONK)
26 #include "android/log.h"
27 #define ALOG(args...) __android_log_print(ANDROID_LOG_INFO, "MediaPluginHost" , ## args)
28 #else
29 #define ALOG(args...) /* do nothing */
30 #endif
32 using namespace MPAPI;
34 Decoder::Decoder() :
35 mResource(nullptr), mPrivate(nullptr)
36 {
37 }
39 namespace mozilla {
41 static char* GetResource(Decoder *aDecoder)
42 {
43 return static_cast<char*>(aDecoder->mResource);
44 }
46 class GetIntPrefEvent : public nsRunnable {
47 public:
48 GetIntPrefEvent(const char* aPref, int32_t* aResult)
49 : mPref(aPref), mResult(aResult) {}
50 NS_IMETHOD Run() {
51 return Preferences::GetInt(mPref, mResult);
52 }
53 private:
54 const char* mPref;
55 int32_t* mResult;
56 };
58 static bool GetIntPref(const char* aPref, int32_t* aResult)
59 {
60 // GetIntPref() is called on the decoder thread, but the Preferences API
61 // can only be called on the main thread. Post a runnable and wait.
62 NS_ENSURE_TRUE(aPref, false);
63 NS_ENSURE_TRUE(aResult, false);
64 nsCOMPtr<nsIRunnable> event = new GetIntPrefEvent(aPref, aResult);
65 return NS_SUCCEEDED(NS_DispatchToMainThread(event, NS_DISPATCH_SYNC));
66 }
68 static bool
69 GetSystemInfoString(const char *aKey, char *aResult, size_t aResultLength)
70 {
71 NS_ENSURE_TRUE(aKey, false);
72 NS_ENSURE_TRUE(aResult, false);
74 nsCOMPtr<nsIPropertyBag2> infoService = do_GetService("@mozilla.org/system-info;1");
75 NS_ASSERTION(infoService, "Could not find a system info service");
77 nsAutoCString key(aKey);
78 nsAutoCString info;
79 nsresult rv = infoService->GetPropertyAsACString(NS_ConvertUTF8toUTF16(key),
80 info);
82 NS_ENSURE_SUCCESS(rv, false);
84 strncpy(aResult, info.get(), aResultLength);
86 return true;
87 }
89 static PluginHost sPluginHost = {
90 nullptr,
91 nullptr,
92 nullptr,
93 nullptr,
94 GetIntPref,
95 GetSystemInfoString
96 };
98 // Return true if Omx decoding is supported on the device. This checks the
99 // built in whitelist/blacklist and preferences to see if that is overridden.
100 static bool IsOmxSupported()
101 {
102 bool forceEnabled =
103 Preferences::GetBool("stagefright.force-enabled", false);
104 bool disabled =
105 Preferences::GetBool("stagefright.disabled", false);
107 if (disabled) {
108 NS_WARNING("XXX stagefright disabled\n");
109 return false;
110 }
112 ScopedGfxFeatureReporter reporter("Stagefright", forceEnabled);
114 if (!forceEnabled) {
115 nsCOMPtr<nsIGfxInfo> gfxInfo = do_GetService("@mozilla.org/gfx/info;1");
116 if (gfxInfo) {
117 int32_t status;
118 if (NS_SUCCEEDED(gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_STAGEFRIGHT, &status))) {
119 if (status != nsIGfxInfo::FEATURE_NO_INFO) {
120 NS_WARNING("XXX stagefright blacklisted\n");
121 return false;
122 }
123 }
124 }
125 }
127 reporter.SetSuccessful();
128 return true;
129 }
131 // Return the name of the shared library that implements Omx based decoding. This varies
132 // depending on libstagefright version installed on the device and whether it is B2G vs Android.
133 // nullptr is returned if Omx decoding is not supported on the device,
134 static const char* GetOmxLibraryName()
135 {
136 #if defined(ANDROID) && !defined(MOZ_WIDGET_GONK)
137 nsCOMPtr<nsIPropertyBag2> infoService = do_GetService("@mozilla.org/system-info;1");
138 NS_ASSERTION(infoService, "Could not find a system info service");
140 int32_t version;
141 nsresult rv = infoService->GetPropertyAsInt32(NS_LITERAL_STRING("version"), &version);
142 if (NS_SUCCEEDED(rv)) {
143 ALOG("Android Version is: %d", version);
144 }
146 nsAutoString release_version;
147 rv = infoService->GetPropertyAsAString(NS_LITERAL_STRING("release_version"), release_version);
148 if (NS_SUCCEEDED(rv)) {
149 ALOG("Android Release Version is: %s", NS_LossyConvertUTF16toASCII(release_version).get());
150 }
152 nsAutoString device;
153 rv = infoService->GetPropertyAsAString(NS_LITERAL_STRING("device"), device);
154 if (NS_SUCCEEDED(rv)) {
155 ALOG("Android Device is: %s", NS_LossyConvertUTF16toASCII(device).get());
156 }
158 nsAutoString manufacturer;
159 rv = infoService->GetPropertyAsAString(NS_LITERAL_STRING("manufacturer"), manufacturer);
160 if (NS_SUCCEEDED(rv)) {
161 ALOG("Android Manufacturer is: %s", NS_LossyConvertUTF16toASCII(manufacturer).get());
162 }
164 nsAutoString hardware;
165 rv = infoService->GetPropertyAsAString(NS_LITERAL_STRING("hardware"), hardware);
166 if (NS_SUCCEEDED(rv)) {
167 ALOG("Android Hardware is: %s", NS_LossyConvertUTF16toASCII(hardware).get());
168 }
169 #endif
171 if (!IsOmxSupported())
172 return nullptr;
174 #if defined(ANDROID) && !defined(MOZ_WIDGET_GONK)
175 if (version >= 17) {
176 return "libomxpluginkk.so";
177 }
178 else if (version == 13 || version == 12 || version == 11) {
179 return "libomxpluginhc.so";
180 }
181 else if (version == 10 && release_version >= NS_LITERAL_STRING("2.3.6")) {
182 // Gingerbread versions from 2.3.6 and above have a different DataSource
183 // layout to those on 2.3.5 and below.
184 return "libomxplugingb.so";
185 }
186 else if (version == 10 && release_version >= NS_LITERAL_STRING("2.3.4") &&
187 device.Find("HTC") == 0) {
188 // HTC devices running Gingerbread 2.3.4+ (HTC Desire HD, HTC Evo Design, etc) seem to
189 // use a newer version of Gingerbread libstagefright than other 2.3.4 devices.
190 return "libomxplugingb.so";
191 }
192 else if (version == 9 || (version == 10 && release_version <= NS_LITERAL_STRING("2.3.5"))) {
193 // Gingerbread versions from 2.3.5 and below have a different DataSource
194 // than 2.3.6 and above.
195 return "libomxplugingb235.so";
196 }
197 else if (version == 8) {
198 // Froyo
199 return "libomxpluginfroyo.so";
200 }
201 else if (version < 8) {
202 // Below Froyo not supported
203 return nullptr;
204 }
206 // Ice Cream Sandwich and Jellybean
207 return "libomxplugin.so";
209 #elif defined(ANDROID) && defined(MOZ_WIDGET_GONK)
210 return "libomxplugin.so";
211 #else
212 return nullptr;
213 #endif
214 }
216 MediaPluginHost::MediaPluginHost() {
217 MOZ_COUNT_CTOR(MediaPluginHost);
219 mResourceServer = MediaResourceServer::Start();
221 const char* name = GetOmxLibraryName();
222 ALOG("Loading OMX Plugin: %s", name ? name : "nullptr");
223 if (name) {
224 char *path = PR_GetLibraryFilePathname("libxul.so", (PRFuncPtr) GetOmxLibraryName);
225 PRLibrary *lib = nullptr;
226 if (path) {
227 nsAutoCString libpath(path);
228 PR_Free(path);
229 int32_t slash = libpath.RFindChar('/');
230 if (slash != kNotFound) {
231 libpath.Truncate(slash + 1);
232 libpath.Append(name);
233 lib = PR_LoadLibrary(libpath.get());
234 }
235 }
236 if (!lib)
237 lib = PR_LoadLibrary(name);
239 if (lib) {
240 Manifest *manifest = static_cast<Manifest *>(PR_FindSymbol(lib, "MPAPI_MANIFEST"));
241 if (manifest) {
242 mPlugins.AppendElement(manifest);
243 ALOG("OMX plugin successfully loaded");
244 }
245 }
246 }
247 }
249 MediaPluginHost::~MediaPluginHost() {
250 mResourceServer->Stop();
251 MOZ_COUNT_DTOR(MediaPluginHost);
252 }
254 bool MediaPluginHost::FindDecoder(const nsACString& aMimeType, const char* const** aCodecs)
255 {
256 const char *chars;
257 size_t len = NS_CStringGetData(aMimeType, &chars, nullptr);
258 for (size_t n = 0; n < mPlugins.Length(); ++n) {
259 Manifest *plugin = mPlugins[n];
260 const char* const *codecs;
261 if (plugin->CanDecode(chars, len, &codecs)) {
262 if (aCodecs)
263 *aCodecs = codecs;
264 return true;
265 }
266 }
267 return false;
268 }
270 MPAPI::Decoder *MediaPluginHost::CreateDecoder(MediaResource *aResource, const nsACString& aMimeType)
271 {
272 NS_ENSURE_TRUE(aResource, nullptr);
274 nsAutoPtr<Decoder> decoder(new Decoder());
275 if (!decoder) {
276 return nullptr;
277 }
279 const char *chars;
280 size_t len = NS_CStringGetData(aMimeType, &chars, nullptr);
281 for (size_t n = 0; n < mPlugins.Length(); ++n) {
282 Manifest *plugin = mPlugins[n];
283 const char* const *codecs;
284 if (!plugin->CanDecode(chars, len, &codecs)) {
285 continue;
286 }
288 nsCString url;
289 nsresult rv = mResourceServer->AddResource(aResource, url);
290 if (NS_FAILED (rv)) continue;
292 decoder->mResource = strdup(url.get());
293 if (plugin->CreateDecoder(&sPluginHost, decoder, chars, len)) {
294 aResource->AddRef();
295 return decoder.forget();
296 }
297 }
299 return nullptr;
300 }
302 void MediaPluginHost::DestroyDecoder(Decoder *aDecoder)
303 {
304 aDecoder->DestroyDecoder(aDecoder);
305 char* resource = GetResource(aDecoder);
306 if (resource) {
307 // resource *shouldn't* be null, but check anyway just in case the plugin
308 // decoder does something stupid.
309 mResourceServer->RemoveResource(nsCString(resource));
310 free(resource);
311 }
312 delete aDecoder;
313 }
315 MediaPluginHost *sMediaPluginHost = nullptr;
316 MediaPluginHost *GetMediaPluginHost()
317 {
318 if (!sMediaPluginHost) {
319 sMediaPluginHost = new MediaPluginHost();
320 }
321 return sMediaPluginHost;
322 }
324 void MediaPluginHost::Shutdown()
325 {
326 delete sMediaPluginHost;
327 sMediaPluginHost = nullptr;
328 }
330 } // namespace mozilla