Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/media/gmp/GMPUtils.cpp
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
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
7
#include "GMPUtils.h"
8
#include "nsDirectoryServiceDefs.h"
9
#include "nsIFile.h"
10
#include "nsCOMPtr.h"
11
#include "nsLiteralString.h"
12
#include "nsCRTGlue.h"
13
#include "mozilla/Base64.h"
14
#include "nsISimpleEnumerator.h"
15
#include "prio.h"
16
#include "nsIConsoleService.h"
17
#include "mozIGeckoMediaPluginService.h"
18
#include "GMPService.h"
19
20
namespace mozilla {
21
22
void
23
SplitAt(const char* aDelims,
24
        const nsACString& aInput,
25
        nsTArray<nsCString>& aOutTokens)
26
0
{
27
0
  nsAutoCString str(aInput);
28
0
  char* end = str.BeginWriting();
29
0
  const char* start = nullptr;
30
0
  while (!!(start = NS_strtok(aDelims, &end))) {
31
0
    aOutTokens.AppendElement(nsCString(start));
32
0
  }
33
0
}
34
35
nsCString
36
ToHexString(const uint8_t * aBytes, uint32_t aLength)
37
0
{
38
0
  static const char hex[] = {
39
0
    '0', '1', '2', '3',
40
0
    '4', '5', '6', '7',
41
0
    '8', '9', 'a', 'b',
42
0
    'c', 'd', 'e', 'f'
43
0
  };
44
0
  nsCString str;
45
0
  for (uint32_t i = 0; i < aLength; i++) {
46
0
    char buf[3];
47
0
    buf[0] = hex[(aBytes[i] & 0xf0) >> 4];
48
0
    buf[1] = hex[aBytes[i] & 0x0f];
49
0
    buf[2] = 0;
50
0
    str.AppendASCII(buf);
51
0
  }
52
0
  return str;
53
0
}
54
55
nsCString
56
ToHexString(const nsTArray<uint8_t>& aBytes)
57
0
{
58
0
  return ToHexString(aBytes.Elements(), aBytes.Length());
59
0
}
60
61
bool
62
FileExists(nsIFile* aFile)
63
0
{
64
0
  bool exists = false;
65
0
  return aFile && NS_SUCCEEDED(aFile->Exists(&exists)) && exists;
66
0
}
67
68
DirectoryEnumerator::DirectoryEnumerator(nsIFile* aPath, Mode aMode)
69
  : mMode(aMode)
70
0
{
71
0
  aPath->GetDirectoryEntries(getter_AddRefs(mIter));
72
0
}
73
74
already_AddRefed<nsIFile>
75
DirectoryEnumerator::Next()
76
0
{
77
0
  if (!mIter) {
78
0
    return nullptr;
79
0
  }
80
0
  bool hasMore = false;
81
0
  while (NS_SUCCEEDED(mIter->HasMoreElements(&hasMore)) && hasMore) {
82
0
    nsCOMPtr<nsISupports> supports;
83
0
    nsresult rv = mIter->GetNext(getter_AddRefs(supports));
84
0
    if (NS_FAILED(rv)) {
85
0
      continue;
86
0
    }
87
0
88
0
    nsCOMPtr<nsIFile> path(do_QueryInterface(supports, &rv));
89
0
    if (NS_FAILED(rv)) {
90
0
      continue;
91
0
    }
92
0
93
0
    if (mMode == DirsOnly) {
94
0
      bool isDirectory = false;
95
0
      rv = path->IsDirectory(&isDirectory);
96
0
      if (NS_FAILED(rv) || !isDirectory) {
97
0
        continue;
98
0
      }
99
0
    }
100
0
    return path.forget();
101
0
  }
102
0
  return nullptr;
103
0
}
104
105
bool
106
ReadIntoArray(nsIFile* aFile,
107
              nsTArray<uint8_t>& aOutDst,
108
              size_t aMaxLength)
109
0
{
110
0
  if (!FileExists(aFile)) {
111
0
    return false;
112
0
  }
113
0
114
0
  PRFileDesc* fd = nullptr;
115
0
  nsresult rv = aFile->OpenNSPRFileDesc(PR_RDONLY, 0, &fd);
116
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
117
0
    return false;
118
0
  }
119
0
120
0
  int32_t length = PR_Seek(fd, 0, PR_SEEK_END);
121
0
  PR_Seek(fd, 0, PR_SEEK_SET);
122
0
123
0
  if (length < 0 || (size_t)length > aMaxLength) {
124
0
    NS_WARNING("EME file is longer than maximum allowed length");
125
0
    PR_Close(fd);
126
0
    return false;
127
0
  }
128
0
  aOutDst.SetLength(length);
129
0
  int32_t bytesRead = PR_Read(fd, aOutDst.Elements(), length);
130
0
  PR_Close(fd);
131
0
  return (bytesRead == length);
132
0
}
133
134
bool
135
ReadIntoString(nsIFile* aFile,
136
               nsCString& aOutDst,
137
               size_t aMaxLength)
138
0
{
139
0
  nsTArray<uint8_t> buf;
140
0
  bool rv = ReadIntoArray(aFile, buf, aMaxLength);
141
0
  if (rv) {
142
0
    buf.AppendElement(0); // Append null terminator, required by nsC*String.
143
0
    aOutDst = nsDependentCString((const char*)buf.Elements(), buf.Length() - 1);
144
0
  }
145
0
  return rv;
146
0
}
147
148
bool
149
GMPInfoFileParser::Init(nsIFile* aInfoFile)
150
0
{
151
0
  nsTArray<nsCString> lines;
152
0
  static const size_t MAX_GMP_INFO_FILE_LENGTH = 5 * 1024;
153
0
154
0
  nsAutoCString info;
155
0
  if (!ReadIntoString(aInfoFile, info, MAX_GMP_INFO_FILE_LENGTH)) {
156
0
    NS_WARNING("Failed to read info file in GMP process.");
157
0
    return false;
158
0
  }
159
0
160
0
  // Note: we pass "\r\n" to SplitAt so that we'll split lines delimited
161
0
  // by \n (Unix), \r\n (Windows) and \r (old MacOSX).
162
0
  SplitAt("\r\n", info, lines);
163
0
164
0
  for (nsCString line : lines) {
165
0
    // Field name is the string up to but not including the first ':'
166
0
    // character on the line.
167
0
    int32_t colon = line.FindChar(':');
168
0
    if (colon <= 0) {
169
0
      // Not allowed to be the first character.
170
0
      // Info field name must be at least one character.
171
0
      continue;
172
0
    }
173
0
    nsAutoCString key(Substring(line, 0, colon));
174
0
    ToLowerCase(key);
175
0
    key.Trim(" ");
176
0
177
0
    nsCString* value = new nsCString(Substring(line, colon + 1));
178
0
    value->Trim(" ");
179
0
    mValues.Put(key, value); // Hashtable assumes ownership of value.
180
0
  }
181
0
182
0
  return true;
183
0
}
184
185
bool
186
0
GMPInfoFileParser::Contains(const nsCString& aKey) const {
187
0
  nsCString key(aKey);
188
0
  ToLowerCase(key);
189
0
  return mValues.Contains(key);
190
0
}
191
192
nsCString
193
0
GMPInfoFileParser::Get(const nsCString& aKey) const {
194
0
  MOZ_ASSERT(Contains(aKey));
195
0
  nsCString key(aKey);
196
0
  ToLowerCase(key);
197
0
  nsCString* p = nullptr;
198
0
  if (mValues.Get(key, &p)) {
199
0
    return nsCString(*p);
200
0
  }
201
0
  return EmptyCString();
202
0
}
203
204
bool
205
HaveGMPFor(const nsCString& aAPI,
206
           nsTArray<nsCString>&& aTags)
207
0
{
208
0
  nsCOMPtr<mozIGeckoMediaPluginService> mps =
209
0
    do_GetService("@mozilla.org/gecko-media-plugin-service;1");
210
0
  if (NS_WARN_IF(!mps)) {
211
0
    return false;
212
0
  }
213
0
214
0
  bool hasPlugin = false;
215
0
  if (NS_FAILED(mps->HasPluginForAPI(aAPI, &aTags, &hasPlugin))) {
216
0
    return false;
217
0
  }
218
0
  return hasPlugin;
219
0
}
220
221
void
222
LogToConsole(const nsAString& aMsg)
223
0
{
224
0
  nsCOMPtr<nsIConsoleService> console(
225
0
    do_GetService("@mozilla.org/consoleservice;1"));
226
0
  if (!console) {
227
0
    NS_WARNING("Failed to log message to console.");
228
0
    return;
229
0
  }
230
0
  nsAutoString msg(aMsg);
231
0
  console->LogStringMessage(msg.get());
232
0
}
233
234
RefPtr<AbstractThread>
235
GetGMPAbstractThread()
236
0
{
237
0
  RefPtr<gmp::GeckoMediaPluginService> service =
238
0
    gmp::GeckoMediaPluginService::GetGeckoMediaPluginService();
239
0
  return service ? service->GetAbstractGMPThread() : nullptr;
240
0
}
241
242
static size_t
243
Align16(size_t aNumber)
244
0
{
245
0
  const size_t mask = 15; // Alignment - 1.
246
0
  return (aNumber + mask) & ~mask;
247
0
}
248
249
size_t
250
I420FrameBufferSizePadded(int32_t aWidth, int32_t aHeight)
251
0
{
252
0
  if (aWidth <= 0 || aHeight <= 0 || aWidth > MAX_VIDEO_WIDTH ||
253
0
      aHeight > MAX_VIDEO_HEIGHT) {
254
0
    return 0;
255
0
  }
256
0
257
0
  size_t ySize = Align16(aWidth) * Align16(aHeight);
258
0
  return ySize + (ySize / 4) * 2;
259
0
}
260
261
} // namespace mozilla