Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/base/nsPluginArray.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
5
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7
#include "nsPluginArray.h"
8
9
#include "mozilla/dom/PluginArrayBinding.h"
10
#include "mozilla/dom/PluginBinding.h"
11
#include "mozilla/dom/HiddenPluginEvent.h"
12
13
#include "nsMimeTypeArray.h"
14
#include "Navigator.h"
15
#include "nsIDocShell.h"
16
#include "nsIWebNavigation.h"
17
#include "nsPluginHost.h"
18
#include "nsPluginTags.h"
19
#include "nsIObserverService.h"
20
#include "nsIWeakReference.h"
21
#include "mozilla/Services.h"
22
#include "nsIInterfaceRequestorUtils.h"
23
#include "nsContentUtils.h"
24
#include "nsIPermissionManager.h"
25
#include "nsIDocument.h"
26
#include "nsIBlocklistService.h"
27
28
using namespace mozilla;
29
using namespace mozilla::dom;
30
31
nsPluginArray::nsPluginArray(nsPIDOMWindowInner* aWindow)
32
  : mWindow(aWindow)
33
0
{
34
0
}
35
36
void
37
nsPluginArray::Init()
38
0
{
39
0
  nsCOMPtr<nsIObserverService> obsService =
40
0
    mozilla::services::GetObserverService();
41
0
  if (obsService) {
42
0
    obsService->AddObserver(this, "plugin-info-updated", true);
43
0
  }
44
0
}
45
46
0
nsPluginArray::~nsPluginArray() = default;
47
48
nsPIDOMWindowInner*
49
nsPluginArray::GetParentObject() const
50
0
{
51
0
  MOZ_ASSERT(mWindow);
52
0
  return mWindow;
53
0
}
54
55
JSObject*
56
nsPluginArray::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
57
0
{
58
0
  return PluginArray_Binding::Wrap(aCx, this, aGivenProto);
59
0
}
60
61
NS_IMPL_CYCLE_COLLECTING_ADDREF(nsPluginArray)
62
NS_IMPL_CYCLE_COLLECTING_RELEASE(nsPluginArray)
63
0
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsPluginArray)
64
0
  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
65
0
  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIObserver)
66
0
  NS_INTERFACE_MAP_ENTRY(nsIObserver)
67
0
  NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
68
0
NS_INTERFACE_MAP_END
69
70
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(nsPluginArray,
71
                                      mWindow,
72
                                      mPlugins,
73
                                      mCTPPlugins)
74
75
static void
76
GetPluginMimeTypes(const nsTArray<RefPtr<nsPluginElement> >& aPlugins,
77
                   nsTArray<RefPtr<nsMimeType> >& aMimeTypes)
78
0
{
79
0
  for (uint32_t i = 0; i < aPlugins.Length(); ++i) {
80
0
    nsPluginElement *plugin = aPlugins[i];
81
0
    aMimeTypes.AppendElements(plugin->MimeTypes());
82
0
  }
83
0
}
84
85
static bool
86
operator<(const RefPtr<nsMimeType>& lhs, const RefPtr<nsMimeType>& rhs)
87
0
{
88
0
  // Sort MIME types alphabetically by type name.
89
0
  return lhs->Type() < rhs->Type();
90
0
}
91
92
void
93
nsPluginArray::GetMimeTypes(nsTArray<RefPtr<nsMimeType>>& aMimeTypes)
94
0
{
95
0
  aMimeTypes.Clear();
96
0
97
0
  if (!AllowPlugins()) {
98
0
    return;
99
0
  }
100
0
101
0
  EnsurePlugins();
102
0
103
0
  GetPluginMimeTypes(mPlugins, aMimeTypes);
104
0
105
0
  // Alphabetize the enumeration order of non-hidden MIME types to reduce
106
0
  // fingerprintable entropy based on plugins' installation file times.
107
0
  aMimeTypes.Sort();
108
0
}
109
110
void
111
nsPluginArray::GetCTPMimeTypes(nsTArray<RefPtr<nsMimeType>>& aMimeTypes)
112
0
{
113
0
  aMimeTypes.Clear();
114
0
115
0
  if (!AllowPlugins()) {
116
0
    return;
117
0
  }
118
0
119
0
  EnsurePlugins();
120
0
121
0
  GetPluginMimeTypes(mCTPPlugins, aMimeTypes);
122
0
123
0
  // Alphabetize the enumeration order of non-hidden MIME types to reduce
124
0
  // fingerprintable entropy based on plugins' installation file times.
125
0
  aMimeTypes.Sort();
126
0
}
127
128
nsPluginElement*
129
nsPluginArray::Item(uint32_t aIndex, CallerType aCallerType)
130
0
{
131
0
  bool unused;
132
0
  return IndexedGetter(aIndex, unused, aCallerType);
133
0
}
134
135
nsPluginElement*
136
nsPluginArray::NamedItem(const nsAString& aName, CallerType aCallerType)
137
0
{
138
0
  bool unused;
139
0
  return NamedGetter(aName, unused, aCallerType);
140
0
}
141
142
void
143
nsPluginArray::Refresh(bool aReloadDocuments)
144
0
{
145
0
  RefPtr<nsPluginHost> pluginHost = nsPluginHost::GetInst();
146
0
147
0
  if(!AllowPlugins() || !pluginHost) {
148
0
    return;
149
0
  }
150
0
151
0
  // NS_ERROR_PLUGINS_PLUGINSNOTCHANGED on reloading plugins indicates
152
0
  // that plugins did not change and was not reloaded
153
0
  if (pluginHost->ReloadPlugins() ==
154
0
      NS_ERROR_PLUGINS_PLUGINSNOTCHANGED) {
155
0
    nsTArray<nsCOMPtr<nsIInternalPluginTag> > newPluginTags;
156
0
    pluginHost->GetPlugins(newPluginTags);
157
0
158
0
    // Check if the number of plugins we know about are different from
159
0
    // the number of plugin tags the plugin host knows about. If the
160
0
    // lengths are different, we refresh. This is safe because we're
161
0
    // notified for every plugin enabling/disabling event that
162
0
    // happens, and therefore the lengths will be in sync only when
163
0
    // the both arrays contain the same plugin tags (though as
164
0
    // different types).
165
0
    if (newPluginTags.Length() == mPlugins.Length()) {
166
0
      return;
167
0
    }
168
0
  }
169
0
170
0
  mPlugins.Clear();
171
0
  mCTPPlugins.Clear();
172
0
173
0
  RefPtr<Navigator> navigator = mWindow->Navigator();
174
0
  navigator->RefreshMIMEArray();
175
0
176
0
  nsCOMPtr<nsIWebNavigation> webNav = do_GetInterface(mWindow);
177
0
  if (aReloadDocuments && webNav) {
178
0
    webNav->Reload(nsIWebNavigation::LOAD_FLAGS_NONE);
179
0
  }
180
0
}
181
182
nsPluginElement*
183
nsPluginArray::IndexedGetter(uint32_t aIndex, bool &aFound, CallerType aCallerType)
184
0
{
185
0
  aFound = false;
186
0
187
0
  if (!AllowPlugins() || nsContentUtils::ResistFingerprinting(aCallerType)) {
188
0
    return nullptr;
189
0
  }
190
0
191
0
  EnsurePlugins();
192
0
193
0
  aFound = aIndex < mPlugins.Length();
194
0
195
0
  if (!aFound) {
196
0
    return nullptr;
197
0
  }
198
0
199
0
  return mPlugins[aIndex];
200
0
}
201
202
void
203
nsPluginArray::Invalidate()
204
0
{
205
0
  nsCOMPtr<nsIObserverService> obsService =
206
0
    mozilla::services::GetObserverService();
207
0
  if (obsService) {
208
0
    obsService->RemoveObserver(this, "plugin-info-updated");
209
0
  }
210
0
}
211
212
static nsPluginElement*
213
FindPlugin(const nsTArray<RefPtr<nsPluginElement> >& aPlugins,
214
           const nsAString& aName)
215
0
{
216
0
  for (uint32_t i = 0; i < aPlugins.Length(); ++i) {
217
0
    nsAutoString pluginName;
218
0
    nsPluginElement* plugin = aPlugins[i];
219
0
    plugin->GetName(pluginName);
220
0
221
0
    if (pluginName.Equals(aName)) {
222
0
      return plugin;
223
0
    }
224
0
  }
225
0
226
0
  return nullptr;
227
0
}
228
229
nsPluginElement*
230
nsPluginArray::NamedGetter(const nsAString& aName, bool &aFound,
231
                           CallerType aCallerType)
232
0
{
233
0
  aFound = false;
234
0
235
0
  if (!AllowPlugins() || nsContentUtils::ResistFingerprinting(aCallerType)) {
236
0
    return nullptr;
237
0
  }
238
0
239
0
  EnsurePlugins();
240
0
241
0
  nsPluginElement* plugin = FindPlugin(mPlugins, aName);
242
0
  aFound = (plugin != nullptr);
243
0
  if (!aFound) {
244
0
    nsPluginElement* hiddenPlugin = FindPlugin(mCTPPlugins, aName);
245
0
    if (hiddenPlugin) {
246
0
      NotifyHiddenPluginTouched(hiddenPlugin);
247
0
    }
248
0
  }
249
0
  return plugin;
250
0
}
251
252
void nsPluginArray::NotifyHiddenPluginTouched(nsPluginElement* aHiddenElement)
253
0
{
254
0
  HiddenPluginEventInit init;
255
0
  init.mTag = aHiddenElement->PluginTag();
256
0
  nsCOMPtr<nsIDocument> doc = aHiddenElement->GetParentObject()->GetDoc();
257
0
  RefPtr<HiddenPluginEvent> event =
258
0
    HiddenPluginEvent::Constructor(doc, NS_LITERAL_STRING("HiddenPlugin"), init);
259
0
  event->SetTarget(doc);
260
0
  event->SetTrusted(true);
261
0
  event->WidgetEventPtr()->mFlags.mOnlyChromeDispatch = true;
262
0
  doc->DispatchEvent(*event);
263
0
}
264
265
uint32_t
266
nsPluginArray::Length(CallerType aCallerType)
267
0
{
268
0
  if (!AllowPlugins() || nsContentUtils::ResistFingerprinting(aCallerType)) {
269
0
    return 0;
270
0
  }
271
0
272
0
  EnsurePlugins();
273
0
274
0
  return mPlugins.Length();
275
0
}
276
277
void
278
nsPluginArray::GetSupportedNames(nsTArray<nsString>& aRetval,
279
                                 CallerType aCallerType)
280
0
{
281
0
  aRetval.Clear();
282
0
283
0
  if (!AllowPlugins() || nsContentUtils::ResistFingerprinting(aCallerType)) {
284
0
    return;
285
0
  }
286
0
287
0
  for (uint32_t i = 0; i < mPlugins.Length(); ++i) {
288
0
    nsAutoString pluginName;
289
0
    mPlugins[i]->GetName(pluginName);
290
0
291
0
    aRetval.AppendElement(pluginName);
292
0
  }
293
0
}
294
295
NS_IMETHODIMP
296
nsPluginArray::Observe(nsISupports *aSubject, const char *aTopic,
297
0
                       const char16_t *aData) {
298
0
  if (!nsCRT::strcmp(aTopic, "plugin-info-updated")) {
299
0
    Refresh(false);
300
0
  }
301
0
302
0
  return NS_OK;
303
0
}
304
305
bool
306
nsPluginArray::AllowPlugins() const
307
0
{
308
0
  if (!mWindow) {
309
0
    return false;
310
0
  }
311
0
  nsCOMPtr<nsIDocument> doc = mWindow->GetDoc();
312
0
  if (!doc) {
313
0
    return false;
314
0
  }
315
0
316
0
  return doc->GetAllowPlugins();
317
0
}
318
319
static bool
320
operator<(const RefPtr<nsPluginElement>& lhs,
321
          const RefPtr<nsPluginElement>& rhs)
322
0
{
323
0
  // Sort plugins alphabetically by name.
324
0
  return lhs->PluginTag()->Name() < rhs->PluginTag()->Name();
325
0
}
326
327
static bool
328
0
PluginShouldBeHidden(const nsCString& aName) {
329
0
  // This only supports one hidden plugin
330
0
  nsAutoCString value;
331
0
  Preferences::GetCString("plugins.navigator.hidden_ctp_plugin", value);
332
0
  return value.Equals(aName);
333
0
}
334
335
void
336
nsPluginArray::EnsurePlugins()
337
0
{
338
0
  if (!mPlugins.IsEmpty() || !mCTPPlugins.IsEmpty()) {
339
0
    // We already have an array of plugin elements.
340
0
    return;
341
0
  }
342
0
343
0
  RefPtr<nsPluginHost> pluginHost = nsPluginHost::GetInst();
344
0
  if (!pluginHost) {
345
0
    // We have no plugin host.
346
0
    return;
347
0
  }
348
0
349
0
  nsTArray<nsCOMPtr<nsIInternalPluginTag> > pluginTags;
350
0
  pluginHost->GetPlugins(pluginTags);
351
0
352
0
  // need to wrap each of these with a nsPluginElement, which is
353
0
  // scriptable.
354
0
  for (uint32_t i = 0; i < pluginTags.Length(); ++i) {
355
0
    nsCOMPtr<nsPluginTag> pluginTag = do_QueryInterface(pluginTags[i]);
356
0
    if (!pluginTag) {
357
0
      mPlugins.AppendElement(new nsPluginElement(mWindow, pluginTags[i]));
358
0
    } else if (pluginTag->IsActive()) {
359
0
      uint32_t permission = nsIPermissionManager::ALLOW_ACTION;
360
0
      uint32_t blocklistState;
361
0
      if (pluginTag->IsClicktoplay() &&
362
0
          NS_SUCCEEDED(pluginTag->GetBlocklistState(&blocklistState)) &&
363
0
          blocklistState == nsIBlocklistService::STATE_NOT_BLOCKED) {
364
0
        nsCString name;
365
0
        pluginTag->GetName(name);
366
0
        if (PluginShouldBeHidden(name)) {
367
0
          RefPtr<nsPluginHost> pluginHost = nsPluginHost::GetInst();
368
0
          nsCString permString;
369
0
          nsresult rv = pluginHost->GetPermissionStringForTag(pluginTag, 0, permString);
370
0
          if (rv == NS_OK) {
371
0
            nsCOMPtr<nsIDocument> currentDoc = mWindow->GetExtantDoc();
372
0
373
0
            // The top-level content document gets the final say on whether or not
374
0
            // a plugin is going to be hidden or not, regardless of the origin
375
0
            // that a subframe is hosted at. This is to avoid spamming the user
376
0
            // with the hidden plugin notification bar when third-party iframes
377
0
            // attempt to access navigator.plugins after the user has already
378
0
            // expressed that the top-level document has this permission.
379
0
            nsCOMPtr<nsIDocument> topDoc = currentDoc->GetTopLevelContentDocument();
380
0
381
0
            if (topDoc) {
382
0
              nsIPrincipal* principal = topDoc->NodePrincipal();
383
0
              nsCOMPtr<nsIPermissionManager> permMgr = services::GetPermissionManager();
384
0
              permMgr->TestPermissionFromPrincipal(principal, permString.get(), &permission);
385
0
            }
386
0
          }
387
0
        }
388
0
      }
389
0
      if (permission == nsIPermissionManager::ALLOW_ACTION) {
390
0
        mPlugins.AppendElement(new nsPluginElement(mWindow, pluginTags[i]));
391
0
      } else {
392
0
        mCTPPlugins.AppendElement(new nsPluginElement(mWindow, pluginTags[i]));
393
0
      }
394
0
    }
395
0
  }
396
0
397
0
  if (mPlugins.Length() == 0 && mCTPPlugins.Length() != 0) {
398
0
    nsCOMPtr<nsPluginTag> hiddenTag = new nsPluginTag("Hidden Plugin", nullptr, "dummy.plugin", nullptr, nullptr,
399
0
                                                      nullptr, nullptr, nullptr, 0, 0, false,
400
0
                                                      nsIBlocklistService::STATE_NOT_BLOCKED);
401
0
    mPlugins.AppendElement(new nsPluginElement(mWindow, hiddenTag));
402
0
  }
403
0
404
0
  // Alphabetize the enumeration order of non-hidden plugins to reduce
405
0
  // fingerprintable entropy based on plugins' installation file times.
406
0
  mPlugins.Sort();
407
0
}
408
// nsPluginElement implementation.
409
410
NS_IMPL_CYCLE_COLLECTING_ADDREF(nsPluginElement)
411
NS_IMPL_CYCLE_COLLECTING_RELEASE(nsPluginElement)
412
0
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsPluginElement)
413
0
  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
414
0
  NS_INTERFACE_MAP_ENTRY(nsISupports)
415
0
NS_INTERFACE_MAP_END
416
417
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(nsPluginElement, mWindow, mMimeTypes)
418
419
nsPluginElement::nsPluginElement(nsPIDOMWindowInner* aWindow,
420
                                 nsIInternalPluginTag* aPluginTag)
421
  : mWindow(aWindow),
422
    mPluginTag(aPluginTag)
423
0
{
424
0
}
425
426
0
nsPluginElement::~nsPluginElement() = default;
427
428
nsPIDOMWindowInner*
429
nsPluginElement::GetParentObject() const
430
0
{
431
0
  MOZ_ASSERT(mWindow);
432
0
  return mWindow;
433
0
}
434
435
JSObject*
436
nsPluginElement::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
437
0
{
438
0
  return Plugin_Binding::Wrap(aCx, this, aGivenProto);
439
0
}
440
441
void
442
nsPluginElement::GetDescription(nsString& retval) const
443
0
{
444
0
  CopyUTF8toUTF16(mPluginTag->Description(), retval);
445
0
}
446
447
void
448
nsPluginElement::GetFilename(nsString& retval) const
449
0
{
450
0
  CopyUTF8toUTF16(mPluginTag->FileName(), retval);
451
0
}
452
453
void
454
nsPluginElement::GetVersion(nsString& retval) const
455
0
{
456
0
  CopyUTF8toUTF16(mPluginTag->Version(), retval);
457
0
}
458
459
void
460
nsPluginElement::GetName(nsString& retval) const
461
0
{
462
0
  CopyUTF8toUTF16(mPluginTag->Name(), retval);
463
0
}
464
465
nsMimeType*
466
nsPluginElement::Item(uint32_t aIndex)
467
0
{
468
0
  EnsurePluginMimeTypes();
469
0
470
0
  return mMimeTypes.SafeElementAt(aIndex);
471
0
}
472
473
nsMimeType*
474
nsPluginElement::NamedItem(const nsAString& aName)
475
0
{
476
0
  bool unused;
477
0
  return NamedGetter(aName, unused);
478
0
}
479
480
nsMimeType*
481
nsPluginElement::IndexedGetter(uint32_t aIndex, bool &aFound)
482
0
{
483
0
  EnsurePluginMimeTypes();
484
0
485
0
  aFound = aIndex < mMimeTypes.Length();
486
0
487
0
  if (!aFound) {
488
0
    return nullptr;
489
0
  }
490
0
491
0
  return mMimeTypes[aIndex];
492
0
}
493
494
nsMimeType*
495
nsPluginElement::NamedGetter(const nsAString& aName, bool &aFound)
496
0
{
497
0
  EnsurePluginMimeTypes();
498
0
499
0
  aFound = false;
500
0
501
0
  for (uint32_t i = 0; i < mMimeTypes.Length(); ++i) {
502
0
    if (mMimeTypes[i]->Type().Equals(aName)) {
503
0
      aFound = true;
504
0
505
0
      return mMimeTypes[i];
506
0
    }
507
0
  }
508
0
509
0
  return nullptr;
510
0
}
511
512
uint32_t
513
nsPluginElement::Length()
514
0
{
515
0
  EnsurePluginMimeTypes();
516
0
517
0
  return mMimeTypes.Length();
518
0
}
519
520
void
521
nsPluginElement::GetSupportedNames(nsTArray<nsString>& retval)
522
0
{
523
0
  EnsurePluginMimeTypes();
524
0
525
0
  for (uint32_t i = 0; i < mMimeTypes.Length(); ++i) {
526
0
    retval.AppendElement(mMimeTypes[i]->Type());
527
0
  }
528
0
}
529
530
nsTArray<RefPtr<nsMimeType> >&
531
nsPluginElement::MimeTypes()
532
0
{
533
0
  EnsurePluginMimeTypes();
534
0
535
0
  return mMimeTypes;
536
0
}
537
538
void
539
nsPluginElement::EnsurePluginMimeTypes()
540
0
{
541
0
  if (!mMimeTypes.IsEmpty()) {
542
0
    return;
543
0
  }
544
0
545
0
  if (mPluginTag->MimeTypes().Length() != mPluginTag->MimeDescriptions().Length() ||
546
0
      mPluginTag->MimeTypes().Length() != mPluginTag->Extensions().Length()) {
547
0
    MOZ_ASSERT(false, "mime type arrays expected to be the same length");
548
0
    return;
549
0
  }
550
0
551
0
  for (uint32_t i = 0; i < mPluginTag->MimeTypes().Length(); ++i) {
552
0
    NS_ConvertUTF8toUTF16 type(mPluginTag->MimeTypes()[i]);
553
0
    NS_ConvertUTF8toUTF16 description(mPluginTag->MimeDescriptions()[i]);
554
0
    NS_ConvertUTF8toUTF16 extension(mPluginTag->Extensions()[i]);
555
0
556
0
    mMimeTypes.AppendElement(new nsMimeType(mWindow, this, type, description,
557
0
                                            extension));
558
0
  }
559
0
}