Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/plugins/base/nsPluginHost.cpp
Line
Count
Source (jump to first uncovered line)
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
/* nsPluginHost.cpp - top-level plugin management code */
7
8
#include "nscore.h"
9
#include "nsPluginHost.h"
10
11
#include <cstdlib>
12
#include <stdio.h>
13
#include "prio.h"
14
#include "nsNPAPIPlugin.h"
15
#include "nsNPAPIPluginStreamListener.h"
16
#include "nsNPAPIPluginInstance.h"
17
#include "nsPluginInstanceOwner.h"
18
#include "nsObjectLoadingContent.h"
19
#include "nsIHTTPHeaderListener.h"
20
#include "nsIHttpHeaderVisitor.h"
21
#include "nsIObserverService.h"
22
#include "nsIHttpProtocolHandler.h"
23
#include "nsIHttpChannel.h"
24
#include "nsIUploadChannel.h"
25
#include "nsIByteRangeRequest.h"
26
#include "nsIStreamListener.h"
27
#include "nsIInputStream.h"
28
#include "nsIOutputStream.h"
29
#include "nsIURL.h"
30
#include "nsTArray.h"
31
#include "nsReadableUtils.h"
32
#include "nsIStreamConverterService.h"
33
#include "nsIFile.h"
34
#if defined(XP_MACOSX)
35
#include "nsILocalFileMac.h"
36
#endif
37
#include "nsISeekableStream.h"
38
#include "nsNetUtil.h"
39
#include "nsIFileStreams.h"
40
#include "nsISimpleEnumerator.h"
41
#include "nsIStringStream.h"
42
#include "nsIProgressEventSink.h"
43
#include "nsIDocument.h"
44
#include "nsPluginLogging.h"
45
#include "nsIScriptChannel.h"
46
#include "nsIBlocklistService.h"
47
#include "nsVersionComparator.h"
48
#include "nsIObjectLoadingContent.h"
49
#include "nsIWritablePropertyBag2.h"
50
#include "nsICategoryManager.h"
51
#include "nsPluginStreamListenerPeer.h"
52
#include "mozilla/NullPrincipal.h"
53
#include "mozilla/dom/ContentChild.h"
54
#include "mozilla/dom/ContentParent.h"
55
#include "mozilla/dom/Element.h"
56
#include "mozilla/dom/FakePluginTagInitBinding.h"
57
#include "mozilla/ClearOnShutdown.h"
58
#include "mozilla/LoadInfo.h"
59
#include "mozilla/plugins/PluginBridge.h"
60
#include "mozilla/plugins/PluginTypes.h"
61
#include "mozilla/Preferences.h"
62
#include "mozilla/ipc/URIUtils.h"
63
64
#include "nsEnumeratorUtils.h"
65
#include "nsXPCOM.h"
66
#include "nsXPCOMCID.h"
67
#include "nsISupportsPrimitives.h"
68
69
#include "nsXULAppAPI.h"
70
#include "nsIXULRuntime.h"
71
72
// for the dialog
73
#include "nsIWindowWatcher.h"
74
#include "nsIDOMWindow.h"
75
76
#include "nsNetCID.h"
77
#include "mozilla/Sprintf.h"
78
#include "nsThreadUtils.h"
79
#include "nsIInputStreamTee.h"
80
#include "nsQueryObject.h"
81
82
#include "nsDirectoryServiceDefs.h"
83
#include "nsAppDirectoryServiceDefs.h"
84
#include "nsPluginDirServiceProvider.h"
85
86
#include "nsUnicharUtils.h"
87
#include "nsPluginManifestLineReader.h"
88
89
#include "nsIWeakReferenceUtils.h"
90
#include "nsIPresShell.h"
91
#include "nsPluginNativeWindow.h"
92
#include "nsIContentPolicy.h"
93
#include "nsContentPolicyUtils.h"
94
#include "mozilla/TimeStamp.h"
95
#include "mozilla/Telemetry.h"
96
#include "nsIImageLoadingContent.h"
97
#include "mozilla/Preferences.h"
98
#include "nsVersionComparator.h"
99
100
#include "mozilla/dom/Promise.h"
101
102
#if defined(XP_WIN)
103
#include "nsIWindowMediator.h"
104
#include "nsIBaseWindow.h"
105
#include "windows.h"
106
#include "winbase.h"
107
#endif
108
109
#include "npapi.h"
110
111
using namespace mozilla;
112
using mozilla::TimeStamp;
113
using mozilla::plugins::FakePluginTag;
114
using mozilla::plugins::PluginTag;
115
using mozilla::dom::FakePluginTagInit;
116
using mozilla::dom::FakePluginMimeEntry;
117
using mozilla::dom::Promise;
118
119
// Null out a strong ref to a linked list iteratively to avoid
120
// exhausting the stack (bug 486349).
121
#define NS_ITERATIVE_UNREF_LIST(type_, list_, mNext_)                \
122
0
  {                                                                  \
123
0
    while (list_) {                                                  \
124
0
      type_ temp = list_->mNext_;                                    \
125
0
      list_->mNext_ = nullptr;                                       \
126
0
      list_ = temp;                                                  \
127
0
    }                                                                \
128
0
  }
129
130
static const char *kPrefWhitelist = "plugin.allowed_types";
131
static const char *kPrefLoadInParentPrefix = "plugin.load_in_parent_process.";
132
static const char *kPrefDisableFullPage = "plugin.disable_full_page_plugin_for_types";
133
134
// How long we wait before unloading an idle plugin process.
135
// Defaults to 30 seconds.
136
static const char *kPrefUnloadPluginTimeoutSecs = "dom.ipc.plugins.unloadTimeoutSecs";
137
static const uint32_t kDefaultPluginUnloadingTimeout = 30;
138
139
static const char *kPluginRegistryVersion = "0.19";
140
141
static const char kDirectoryServiceContractID[] = "@mozilla.org/file/directory_service;1";
142
143
0
#define kPluginRegistryFilename NS_LITERAL_CSTRING("pluginreg.dat")
144
145
LazyLogModule nsPluginLogging::gNPNLog(NPN_LOG_NAME);
146
LazyLogModule nsPluginLogging::gNPPLog(NPP_LOG_NAME);
147
LazyLogModule nsPluginLogging::gPluginLog(PLUGIN_LOG_NAME);
148
149
// #defines for plugin cache and prefs
150
0
#define NS_PREF_MAX_NUM_CACHED_INSTANCES "browser.plugins.max_num_cached_plugins"
151
// Raise this from '10' to '50' to work around a bug in Apple's current Java
152
// plugins on OS X Lion and SnowLeopard.  See bug 705931.
153
0
#define DEFAULT_NUMBER_OF_STOPPED_INSTANCES 50
154
155
nsIFile *nsPluginHost::sPluginTempDir;
156
StaticRefPtr<nsPluginHost> nsPluginHost::sInst;
157
158
/* to cope with short read */
159
/* we should probably put this into a global library now that this is the second
160
   time we need this. */
161
static
162
int32_t
163
busy_beaver_PR_Read(PRFileDesc *fd, void * start, int32_t len)
164
0
{
165
0
    int n;
166
0
    int32_t remaining = len;
167
0
168
0
    while (remaining > 0)
169
0
    {
170
0
        n = PR_Read(fd, start, remaining);
171
0
        if (n < 0)
172
0
        {
173
0
            /* may want to repeat if errno == EINTR */
174
0
            if( (len - remaining) == 0 ) // no octet is ever read
175
0
                return -1;
176
0
            break;
177
0
        }
178
0
        remaining -= n;
179
0
        char *cp = (char *) start;
180
0
        cp += n;
181
0
        start = cp;
182
0
    }
183
0
    return len - remaining;
184
0
}
185
186
NS_IMPL_ISUPPORTS0(nsInvalidPluginTag)
187
188
nsInvalidPluginTag::nsInvalidPluginTag(const char* aFullPath, int64_t aLastModifiedTime)
189
: mFullPath(aFullPath),
190
  mLastModifiedTime(aLastModifiedTime),
191
  mSeen(false)
192
0
{}
193
194
0
nsInvalidPluginTag::~nsInvalidPluginTag() = default;
195
196
// Helper to check for a MIME in a comma-delimited preference
197
static bool
198
IsTypeInList(const nsCString& aMimeType, nsCString aTypeList)
199
0
{
200
0
  nsAutoCString searchStr;
201
0
  searchStr.Assign(',');
202
0
  searchStr.Append(aTypeList);
203
0
  searchStr.Append(',');
204
0
205
0
  nsACString::const_iterator start, end;
206
0
207
0
  searchStr.BeginReading(start);
208
0
  searchStr.EndReading(end);
209
0
210
0
  nsAutoCString commaSeparated;
211
0
  commaSeparated.Assign(',');
212
0
  commaSeparated += aMimeType;
213
0
  commaSeparated.Append(',');
214
0
215
0
  // Lower-case the search string and MIME type to properly handle a mixed-case
216
0
  // type, as MIME types are case insensitive.
217
0
  ToLowerCase(searchStr);
218
0
  ToLowerCase(commaSeparated);
219
0
220
0
  return FindInReadable(commaSeparated, start, end);
221
0
}
222
223
// flat file reg funcs
224
static
225
bool ReadSectionHeader(nsPluginManifestLineReader& reader, const char *token)
226
0
{
227
0
  do {
228
0
    if (*reader.LinePtr() == '[') {
229
0
      char* p = reader.LinePtr() + (reader.LineLength() - 1);
230
0
      if (*p != ']')
231
0
        break;
232
0
      *p = 0;
233
0
234
0
      char* values[1];
235
0
      if (1 != reader.ParseLine(values, 1))
236
0
        break;
237
0
      // ignore the leading '['
238
0
      if (PL_strcmp(values[0]+1, token)) {
239
0
        break; // it's wrong token
240
0
      }
241
0
      return true;
242
0
    }
243
0
  } while (reader.NextLine());
244
0
  return false;
245
0
}
246
247
static bool UnloadPluginsASAP()
248
0
{
249
0
  return (Preferences::GetUint(kPrefUnloadPluginTimeoutSecs, kDefaultPluginUnloadingTimeout) == 0);
250
0
}
251
252
namespace mozilla {
253
namespace plugins {
254
class BlocklistPromiseHandler final : public mozilla::dom::PromiseNativeHandler
255
{
256
  public:
257
    NS_DECL_ISUPPORTS
258
259
    BlocklistPromiseHandler(nsPluginTag *aTag, const bool aShouldSoftblock)
260
      : mTag(aTag)
261
      , mShouldDisableWhenSoftblocked(aShouldSoftblock)
262
0
    {
263
0
      MOZ_ASSERT(mTag, "Should always be passed a plugin tag");
264
0
      sPendingBlocklistStateRequests++;
265
0
    }
266
267
    void
268
    MaybeWriteBlocklistChanges()
269
0
    {
270
0
      // We're called immediately when the promise resolves/rejects, and (as a backup)
271
0
      // when the handler is destroyed. To ensure we only run once, we use mTag as a
272
0
      // sentinel, setting it to nullptr when we run.
273
0
      if (!mTag) {
274
0
        return;
275
0
      }
276
0
      mTag = nullptr;
277
0
      sPendingBlocklistStateRequests--;
278
0
      // If this was the only remaining pending request, check if we need to write
279
0
      // state and if so update the child processes.
280
0
      if (!sPendingBlocklistStateRequests &&
281
0
          sPluginBlocklistStatesChangedSinceLastWrite) {
282
0
        sPluginBlocklistStatesChangedSinceLastWrite = false;
283
0
284
0
        RefPtr<nsPluginHost> host = nsPluginHost::GetInst();
285
0
        // Write the changed list to disk:
286
0
        host->WritePluginInfo();
287
0
288
0
        // We update blocklist info in content processes asynchronously
289
0
        // by just sending a new plugin list to content.
290
0
        host->IncrementChromeEpoch();
291
0
        host->SendPluginsToContent();
292
0
      }
293
0
    }
294
295
    void
296
    ResolvedCallback(JSContext *aCx, JS::Handle<JS::Value> aValue) override
297
0
    {
298
0
      if (!aValue.isInt32()) {
299
0
        MOZ_ASSERT(false, "Blocklist should always return int32");
300
0
        return;
301
0
      }
302
0
      int32_t newState = aValue.toInt32();
303
0
      MOZ_ASSERT(newState >= 0 && newState < nsIBlocklistService::STATE_MAX,
304
0
        "Shouldn't get an out of bounds blocklist state");
305
0
306
0
      // Check the old and new state and see if there was a change:
307
0
      uint32_t oldState = mTag->GetBlocklistState();
308
0
      bool changed = oldState != (uint32_t)newState;
309
0
      mTag->SetBlocklistState(newState);
310
0
311
0
      if (newState == nsIBlocklistService::STATE_SOFTBLOCKED && mShouldDisableWhenSoftblocked) {
312
0
        mTag->SetEnabledState(nsIPluginTag::STATE_DISABLED);
313
0
        changed = true;
314
0
      }
315
0
      sPluginBlocklistStatesChangedSinceLastWrite |= changed;
316
0
317
0
      MaybeWriteBlocklistChanges();
318
0
    }
319
    void
320
    RejectedCallback(JSContext *aCx, JS::Handle<JS::Value> aValue) override
321
0
    {
322
0
      MOZ_ASSERT(false, "Shouldn't reject plugin blocklist state request");
323
0
      MaybeWriteBlocklistChanges();
324
0
    }
325
326
  private:
327
0
    ~BlocklistPromiseHandler() {
328
0
      // If we have multiple plugins and the last pending request is GC'd
329
0
      // and so never resolves/rejects, ensure we still write the blocklist.
330
0
      MaybeWriteBlocklistChanges();
331
0
    }
332
333
    RefPtr<nsPluginTag> mTag;
334
    bool mShouldDisableWhenSoftblocked;
335
336
    // Whether we changed any of the plugins' blocklist states since
337
    // we last started fetching them (async). This is reset to false
338
    // every time we finish fetching plugin blocklist information.
339
    // When this happens, if the previous value was true, we store the
340
    // updated list on disk and send it to child processes.
341
    static bool sPluginBlocklistStatesChangedSinceLastWrite;
342
    // How many pending blocklist state requests we've got
343
    static uint32_t sPendingBlocklistStateRequests;
344
};
345
346
NS_IMPL_ISUPPORTS0(BlocklistPromiseHandler)
347
348
349
bool BlocklistPromiseHandler::sPluginBlocklistStatesChangedSinceLastWrite = false;
350
uint32_t BlocklistPromiseHandler::sPendingBlocklistStateRequests = 0;
351
} // namespace plugins
352
} // namespace mozilla
353
354
355
nsPluginHost::nsPluginHost()
356
  : mPluginsLoaded(false)
357
  , mOverrideInternalTypes(false)
358
  , mPluginsDisabled(false)
359
  , mPluginEpoch(0)
360
0
{
361
0
  // check to see if pref is set at startup to let plugins take over in
362
0
  // full page mode for certain image mime types that we handle internally
363
0
  mOverrideInternalTypes =
364
0
    Preferences::GetBool("plugin.override_internal_types", false);
365
0
366
0
  mPluginsDisabled = Preferences::GetBool("plugin.disable", false);
367
0
368
0
  Preferences::AddStrongObserver(this, "plugin.disable");
369
0
370
0
  nsCOMPtr<nsIObserverService> obsService =
371
0
    mozilla::services::GetObserverService();
372
0
  if (obsService) {
373
0
    obsService->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
374
0
    if (XRE_IsParentProcess()) {
375
0
      obsService->AddObserver(this, "blocklist-updated", false);
376
0
    }
377
0
  }
378
0
379
#ifdef PLUGIN_LOGGING
380
  MOZ_LOG(nsPluginLogging::gNPNLog, PLUGIN_LOG_ALWAYS,("NPN Logging Active!\n"));
381
  MOZ_LOG(nsPluginLogging::gPluginLog, PLUGIN_LOG_ALWAYS,("General Plugin Logging Active! (nsPluginHost::ctor)\n"));
382
  MOZ_LOG(nsPluginLogging::gNPPLog, PLUGIN_LOG_ALWAYS,("NPP Logging Active!\n"));
383
384
  PLUGIN_LOG(PLUGIN_LOG_ALWAYS,("nsPluginHost::ctor\n"));
385
  PR_LogFlush();
386
#endif
387
388
0
  // Load plugins on creation, as there's a good chance we'll need to send them
389
0
  // to content processes directly after creation.
390
0
  if (XRE_IsParentProcess())
391
0
  {
392
0
    // Always increment the chrome epoch when we bring up the nsPluginHost in
393
0
    // the parent process. If the only plugins we have are cached in
394
0
    // pluginreg.dat, we won't see any plugin changes in LoadPlugins and the
395
0
    // epoch will stay the same between the parent and child process, meaning
396
0
    // plugins will never update in the child process.
397
0
    IncrementChromeEpoch();
398
0
    LoadPlugins();
399
0
  }
400
0
}
401
402
nsPluginHost::~nsPluginHost()
403
0
{
404
0
  PLUGIN_LOG(PLUGIN_LOG_ALWAYS,("nsPluginHost::dtor\n"));
405
0
406
0
  UnloadPlugins();
407
0
}
408
409
NS_IMPL_ISUPPORTS(nsPluginHost,
410
                  nsIPluginHost,
411
                  nsIObserver,
412
                  nsITimerCallback,
413
                  nsISupportsWeakReference,
414
                  nsINamed)
415
416
already_AddRefed<nsPluginHost>
417
nsPluginHost::GetInst()
418
0
{
419
0
  if (!sInst) {
420
0
    sInst = new nsPluginHost();
421
0
    ClearOnShutdown(&sInst);
422
0
  }
423
0
424
0
  return do_AddRef(sInst);
425
0
}
426
427
bool nsPluginHost::IsRunningPlugin(nsPluginTag * aPluginTag)
428
0
{
429
0
  if (!aPluginTag || !aPluginTag->mPlugin) {
430
0
    return false;
431
0
  }
432
0
433
0
  if (aPluginTag->mContentProcessRunningCount) {
434
0
    return true;
435
0
  }
436
0
437
0
  for (uint32_t i = 0; i < mInstances.Length(); i++) {
438
0
    nsNPAPIPluginInstance *instance = mInstances[i].get();
439
0
    if (instance &&
440
0
        instance->GetPlugin() == aPluginTag->mPlugin &&
441
0
        instance->IsRunning()) {
442
0
      return true;
443
0
    }
444
0
  }
445
0
446
0
  return false;
447
0
}
448
449
nsresult nsPluginHost::ReloadPlugins()
450
0
{
451
0
  PLUGIN_LOG(PLUGIN_LOG_NORMAL,
452
0
  ("nsPluginHost::ReloadPlugins Begin\n"));
453
0
454
0
  // If we're calling this from a content process, forward the reload request to
455
0
  // the parent process. If plugins actually changed, it will notify us
456
0
  // asynchronously later.
457
0
  if (XRE_IsContentProcess())
458
0
  {
459
0
    Unused << mozilla::dom::ContentChild::GetSingleton()->SendMaybeReloadPlugins();
460
0
    // In content processes, always signal that plugins have not changed. We
461
0
    // will never know if they changed here unless we make slow synchronous
462
0
    // calls. This information will hopefully only be wrong once, as if there
463
0
    // has been a plugin update, we expect to have gotten notification from the
464
0
    // parent process and everything should be updated by the next time this is
465
0
    // called. See Bug 1337058 for more info.
466
0
    return NS_ERROR_PLUGINS_PLUGINSNOTCHANGED;
467
0
  }
468
0
  // this will create the initial plugin list out of cache
469
0
  // if it was not created yet
470
0
  if (!mPluginsLoaded)
471
0
    return LoadPlugins();
472
0
473
0
  // we are re-scanning plugins. New plugins may have been added, also some
474
0
  // plugins may have been removed, so we should probably shut everything down
475
0
  // but don't touch running (active and not stopped) plugins
476
0
477
0
  // check if plugins changed, no need to do anything else
478
0
  // if no changes to plugins have been made
479
0
  // false instructs not to touch the plugin list, just to
480
0
  // look for possible changes
481
0
  bool pluginschanged = true;
482
0
  FindPlugins(false, &pluginschanged);
483
0
484
0
  // if no changed detected, return an appropriate error code
485
0
  if (!pluginschanged)
486
0
    return NS_ERROR_PLUGINS_PLUGINSNOTCHANGED;
487
0
488
0
  return ActuallyReloadPlugins();
489
0
}
490
491
nsresult
492
nsPluginHost::ActuallyReloadPlugins()
493
0
{
494
0
  nsresult rv = NS_OK;
495
0
496
0
  // shutdown plugins and kill the list if there are no running plugins
497
0
  RefPtr<nsPluginTag> prev;
498
0
  RefPtr<nsPluginTag> next;
499
0
500
0
  for (RefPtr<nsPluginTag> p = mPlugins; p != nullptr;) {
501
0
    next = p->mNext;
502
0
503
0
    // only remove our plugin from the list if it's not running.
504
0
    if (!IsRunningPlugin(p)) {
505
0
      if (p == mPlugins)
506
0
        mPlugins = next;
507
0
      else
508
0
        prev->mNext = next;
509
0
510
0
      p->mNext = nullptr;
511
0
512
0
      // attempt to unload plugins whenever they are removed from the list
513
0
      p->TryUnloadPlugin(false);
514
0
515
0
      p = next;
516
0
      continue;
517
0
    }
518
0
519
0
    prev = p;
520
0
    p = next;
521
0
  }
522
0
523
0
  // set flags
524
0
  mPluginsLoaded = false;
525
0
526
0
  // load them again
527
0
  rv = LoadPlugins();
528
0
529
0
  if (XRE_IsParentProcess())
530
0
  {
531
0
    // If the plugin list changed, update content. If the plugin list changed
532
0
    // for the content process, it will also reload plugins.
533
0
    SendPluginsToContent();
534
0
  }
535
0
536
0
  PLUGIN_LOG(PLUGIN_LOG_NORMAL,
537
0
  ("nsPluginHost::ReloadPlugins End\n"));
538
0
539
0
  return rv;
540
0
}
541
542
0
#define NS_RETURN_UASTRING_SIZE 128
543
544
nsresult nsPluginHost::UserAgent(const char **retstring)
545
0
{
546
0
  static char resultString[NS_RETURN_UASTRING_SIZE];
547
0
  nsresult res;
548
0
549
0
  nsCOMPtr<nsIHttpProtocolHandler> http = do_GetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "http", &res);
550
0
  if (NS_FAILED(res))
551
0
    return res;
552
0
553
0
  nsAutoCString uaString;
554
0
  res = http->GetUserAgent(uaString);
555
0
556
0
  if (NS_SUCCEEDED(res)) {
557
0
    if (NS_RETURN_UASTRING_SIZE > uaString.Length()) {
558
0
      PL_strcpy(resultString, uaString.get());
559
0
    } else {
560
0
      // Copy as much of UA string as we can (terminate at right-most space).
561
0
      PL_strncpy(resultString, uaString.get(), NS_RETURN_UASTRING_SIZE);
562
0
      for (int i = NS_RETURN_UASTRING_SIZE - 1; i >= 0; i--) {
563
0
        if (i == 0) {
564
0
          resultString[NS_RETURN_UASTRING_SIZE - 1] = '\0';
565
0
        }
566
0
        else if (resultString[i] == ' ') {
567
0
          resultString[i] = '\0';
568
0
          break;
569
0
        }
570
0
      }
571
0
    }
572
0
    *retstring = resultString;
573
0
  }
574
0
  else {
575
0
    *retstring = nullptr;
576
0
  }
577
0
578
0
  PLUGIN_LOG(PLUGIN_LOG_NORMAL, ("nsPluginHost::UserAgent return=%s\n", *retstring));
579
0
580
0
  return res;
581
0
}
582
583
nsresult nsPluginHost::GetURL(nsISupports* pluginInst,
584
                              const char* url,
585
                              const char* target,
586
                              nsNPAPIPluginStreamListener* streamListener,
587
                              const char* altHost,
588
                              const char* referrer,
589
                              bool forceJSEnabled)
590
0
{
591
0
  return GetURLWithHeaders(static_cast<nsNPAPIPluginInstance*>(pluginInst),
592
0
                           url, target, streamListener, altHost, referrer,
593
0
                           forceJSEnabled, 0, nullptr);
594
0
}
595
596
nsresult nsPluginHost::GetURLWithHeaders(nsNPAPIPluginInstance* pluginInst,
597
                                         const char* url,
598
                                         const char* target,
599
                                         nsNPAPIPluginStreamListener* streamListener,
600
                                         const char* altHost,
601
                                         const char* referrer,
602
                                         bool forceJSEnabled,
603
                                         uint32_t getHeadersLength,
604
                                         const char* getHeaders)
605
0
{
606
0
  // we can only send a stream back to the plugin (as specified by a
607
0
  // null target) if we also have a nsNPAPIPluginStreamListener to talk to
608
0
  if (!target && !streamListener) {
609
0
    return NS_ERROR_ILLEGAL_VALUE;
610
0
  }
611
0
612
0
  nsresult rv = NS_OK;
613
0
614
0
  if (target) {
615
0
    RefPtr<nsPluginInstanceOwner> owner = pluginInst->GetOwner();
616
0
    if (owner) {
617
0
      rv = owner->GetURL(url, target, nullptr, nullptr, 0, true);
618
0
    }
619
0
  }
620
0
621
0
  if (streamListener) {
622
0
    rv = NewPluginURLStream(NS_ConvertUTF8toUTF16(url), pluginInst,
623
0
                            streamListener, nullptr,
624
0
                            getHeaders, getHeadersLength);
625
0
  }
626
0
  return rv;
627
0
}
628
629
nsresult nsPluginHost::PostURL(nsISupports* pluginInst,
630
                               const char* url,
631
                               uint32_t postDataLen,
632
                               const char* postData,
633
                               const char* target,
634
                               nsNPAPIPluginStreamListener* streamListener,
635
                               const char* altHost,
636
                               const char* referrer,
637
                               bool forceJSEnabled,
638
                               uint32_t postHeadersLength,
639
                               const char* postHeaders)
640
0
{
641
0
  nsresult rv;
642
0
643
0
  // we can only send a stream back to the plugin (as specified
644
0
  // by a null target) if we also have a nsNPAPIPluginStreamListener
645
0
  // to talk to also
646
0
  if (!target && !streamListener)
647
0
    return NS_ERROR_ILLEGAL_VALUE;
648
0
649
0
  nsNPAPIPluginInstance* instance = static_cast<nsNPAPIPluginInstance*>(pluginInst);
650
0
651
0
  nsCOMPtr<nsIInputStream> postStream;
652
0
  char *dataToPost;
653
0
  uint32_t newDataToPostLen;
654
0
  ParsePostBufferToFixHeaders(postData, postDataLen, &dataToPost, &newDataToPostLen);
655
0
  if (!dataToPost)
656
0
    return NS_ERROR_UNEXPECTED;
657
0
658
0
  nsCOMPtr<nsIStringInputStream> sis = do_CreateInstance("@mozilla.org/io/string-input-stream;1", &rv);
659
0
  if (!sis) {
660
0
    free(dataToPost);
661
0
    return rv;
662
0
  }
663
0
664
0
  // data allocated by ParsePostBufferToFixHeaders() is managed and
665
0
  // freed by the string stream.
666
0
  postDataLen = newDataToPostLen;
667
0
  sis->AdoptData(dataToPost, postDataLen);
668
0
  postStream = sis;
669
0
670
0
  if (target) {
671
0
    RefPtr<nsPluginInstanceOwner> owner = instance->GetOwner();
672
0
    if (owner) {
673
0
      rv = owner->GetURL(url, target, postStream,
674
0
                         (void*)postHeaders, postHeadersLength, true);
675
0
    }
676
0
  }
677
0
678
0
  // if we don't have a target, just create a stream.
679
0
  if (streamListener) {
680
0
    rv = NewPluginURLStream(NS_ConvertUTF8toUTF16(url), instance,
681
0
                            streamListener,
682
0
                            postStream, postHeaders, postHeadersLength);
683
0
  }
684
0
  return rv;
685
0
}
686
687
nsresult nsPluginHost::UnloadPlugins()
688
0
{
689
0
  PLUGIN_LOG(PLUGIN_LOG_NORMAL, ("nsPluginHost::UnloadPlugins Called\n"));
690
0
691
0
  if (!mPluginsLoaded)
692
0
    return NS_OK;
693
0
694
0
  // we should call nsIPluginInstance::Stop and nsIPluginInstance::SetWindow
695
0
  // for those plugins who want it
696
0
  DestroyRunningInstances(nullptr);
697
0
698
0
  nsPluginTag *pluginTag;
699
0
  for (pluginTag = mPlugins; pluginTag; pluginTag = pluginTag->mNext) {
700
0
    pluginTag->TryUnloadPlugin(true);
701
0
  }
702
0
703
0
  NS_ITERATIVE_UNREF_LIST(RefPtr<nsPluginTag>, mPlugins, mNext);
704
0
  NS_ITERATIVE_UNREF_LIST(RefPtr<nsPluginTag>, mCachedPlugins, mNext);
705
0
  NS_ITERATIVE_UNREF_LIST(RefPtr<nsInvalidPluginTag>, mInvalidPlugins, mNext);
706
0
707
0
  // Lets remove any of the temporary files that we created.
708
0
  if (sPluginTempDir) {
709
0
    sPluginTempDir->Remove(true);
710
0
    NS_RELEASE(sPluginTempDir);
711
0
  }
712
0
713
#ifdef XP_WIN
714
  if (mPrivateDirServiceProvider) {
715
    nsCOMPtr<nsIDirectoryService> dirService =
716
      do_GetService(kDirectoryServiceContractID);
717
    if (dirService)
718
      dirService->UnregisterProvider(mPrivateDirServiceProvider);
719
    mPrivateDirServiceProvider = nullptr;
720
  }
721
#endif /* XP_WIN */
722
723
0
  mPluginsLoaded = false;
724
0
725
0
  return NS_OK;
726
0
}
727
728
void nsPluginHost::OnPluginInstanceDestroyed(nsPluginTag* aPluginTag)
729
0
{
730
0
  bool hasInstance = false;
731
0
  for (uint32_t i = 0; i < mInstances.Length(); i++) {
732
0
    if (TagForPlugin(mInstances[i]->GetPlugin()) == aPluginTag) {
733
0
      hasInstance = true;
734
0
      break;
735
0
    }
736
0
  }
737
0
738
0
  // We have some options for unloading plugins if they have no instances.
739
0
  //
740
0
  // Unloading plugins immediately can be bad - some plugins retain state
741
0
  // between instances even when there are none. This is largely limited to
742
0
  // going from one page to another, so state is retained without an instance
743
0
  // for only a very short period of time. In order to allow this to work
744
0
  // we don't unload plugins immediately by default. This is supported
745
0
  // via a hidden user pref though.
746
0
  //
747
0
  // Another reason not to unload immediately is that loading is expensive,
748
0
  // and it is better to leave popular plugins loaded.
749
0
  //
750
0
  // Our default behavior is to try to unload a plugin after a pref-controlled
751
0
  // delay once its last instance is destroyed. This seems like a reasonable
752
0
  // compromise that allows us to reclaim memory while allowing short state
753
0
  // retention and avoid perf hits for loading popular plugins.
754
0
  if (!hasInstance) {
755
0
    if (UnloadPluginsASAP()) {
756
0
      aPluginTag->TryUnloadPlugin(false);
757
0
    } else {
758
0
      if (aPluginTag->mUnloadTimer) {
759
0
        aPluginTag->mUnloadTimer->Cancel();
760
0
      } else {
761
0
        aPluginTag->mUnloadTimer = NS_NewTimer();
762
0
      }
763
0
      uint32_t unloadTimeout = Preferences::GetUint(kPrefUnloadPluginTimeoutSecs,
764
0
                                                    kDefaultPluginUnloadingTimeout);
765
0
      aPluginTag->mUnloadTimer->InitWithCallback(this,
766
0
                                                 1000 * unloadTimeout,
767
0
                                                 nsITimer::TYPE_ONE_SHOT);
768
0
    }
769
0
  }
770
0
}
771
772
nsresult
773
nsPluginHost::InstantiatePluginInstance(const nsACString& aMimeType, nsIURI* aURL,
774
                                        nsObjectLoadingContent *aContent,
775
                                        nsPluginInstanceOwner** aOwner)
776
0
{
777
0
  NS_ENSURE_ARG_POINTER(aOwner);
778
0
779
#ifdef PLUGIN_LOGGING
780
  nsAutoCString urlSpec;
781
  if (aURL)
782
    aURL->GetAsciiSpec(urlSpec);
783
784
  MOZ_LOG(nsPluginLogging::gPluginLog, PLUGIN_LOG_NORMAL,
785
        ("nsPluginHost::InstantiatePlugin Begin mime=%s, url=%s\n",
786
         PromiseFlatCString(aMimeType).get(), urlSpec.get()));
787
788
  PR_LogFlush();
789
#endif
790
791
0
  if (aMimeType.IsEmpty()) {
792
0
    MOZ_ASSERT_UNREACHABLE("Attempting to spawn a plugin with no mime type");
793
0
    return NS_ERROR_FAILURE;
794
0
  }
795
0
796
0
  // Plugins are not supported when recording or replaying executions.
797
0
  // See bug 1483232.
798
0
  if (recordreplay::IsRecordingOrReplaying()) {
799
0
    return NS_ERROR_FAILURE;
800
0
  }
801
0
802
0
  RefPtr<nsPluginInstanceOwner> instanceOwner = new nsPluginInstanceOwner();
803
0
  if (!instanceOwner) {
804
0
    return NS_ERROR_OUT_OF_MEMORY;
805
0
  }
806
0
807
0
  nsCOMPtr<nsIContent> ourContent = do_QueryInterface(static_cast<nsIImageLoadingContent*>(aContent));
808
0
  nsresult rv = instanceOwner->Init(ourContent);
809
0
  if (NS_FAILED(rv)) {
810
0
    return rv;
811
0
  }
812
0
813
0
  nsPluginTagType tagType;
814
0
  rv = instanceOwner->GetTagType(&tagType);
815
0
  if (NS_FAILED(rv)) {
816
0
    instanceOwner->Destroy();
817
0
    return rv;
818
0
  }
819
0
820
0
  if (tagType != nsPluginTagType_Embed &&
821
0
      tagType != nsPluginTagType_Object) {
822
0
    instanceOwner->Destroy();
823
0
    return NS_ERROR_FAILURE;
824
0
  }
825
0
826
0
  rv = SetUpPluginInstance(aMimeType, aURL, instanceOwner);
827
0
  if (NS_FAILED(rv)) {
828
0
    instanceOwner->Destroy();
829
0
    return NS_ERROR_FAILURE;
830
0
  }
831
0
832
0
  RefPtr<nsNPAPIPluginInstance> instance;
833
0
  rv = instanceOwner->GetInstance(getter_AddRefs(instance));
834
0
  if (NS_FAILED(rv)) {
835
0
    instanceOwner->Destroy();
836
0
    return rv;
837
0
  }
838
0
839
0
  if (instance) {
840
0
    CreateWidget(instanceOwner);
841
0
  }
842
0
843
0
  // At this point we consider instantiation to be successful. Do not return an error.
844
0
  instanceOwner.forget(aOwner);
845
0
846
#ifdef PLUGIN_LOGGING
847
  nsAutoCString urlSpec2;
848
  if (aURL != nullptr) aURL->GetAsciiSpec(urlSpec2);
849
850
  MOZ_LOG(nsPluginLogging::gPluginLog, PLUGIN_LOG_NORMAL,
851
        ("nsPluginHost::InstantiatePlugin Finished mime=%s, rv=%" PRIu32 ", url=%s\n",
852
         PromiseFlatCString(aMimeType).get(), static_cast<uint32_t>(rv), urlSpec2.get()));
853
854
  PR_LogFlush();
855
#endif
856
857
0
  return NS_OK;
858
0
}
859
860
nsPluginTag*
861
nsPluginHost::FindTagForLibrary(PRLibrary* aLibrary)
862
0
{
863
0
  nsPluginTag* pluginTag;
864
0
  for (pluginTag = mPlugins; pluginTag; pluginTag = pluginTag->mNext) {
865
0
    if (pluginTag->mLibrary == aLibrary) {
866
0
      return pluginTag;
867
0
    }
868
0
  }
869
0
  return nullptr;
870
0
}
871
872
nsPluginTag*
873
nsPluginHost::TagForPlugin(nsNPAPIPlugin* aPlugin)
874
0
{
875
0
  nsPluginTag* pluginTag;
876
0
  for (pluginTag = mPlugins; pluginTag; pluginTag = pluginTag->mNext) {
877
0
    if (pluginTag->mPlugin == aPlugin) {
878
0
      return pluginTag;
879
0
    }
880
0
  }
881
0
  // a plugin should never exist without a corresponding tag
882
0
  NS_ERROR("TagForPlugin has failed");
883
0
  return nullptr;
884
0
}
885
886
nsresult nsPluginHost::SetUpPluginInstance(const nsACString &aMimeType,
887
                                           nsIURI *aURL,
888
                                           nsPluginInstanceOwner *aOwner)
889
0
{
890
0
  NS_ENSURE_ARG_POINTER(aOwner);
891
0
892
0
  nsresult rv = TrySetUpPluginInstance(aMimeType, aURL, aOwner);
893
0
  if (NS_SUCCEEDED(rv)) {
894
0
    return rv;
895
0
  }
896
0
897
0
  // If we failed to load a plugin instance we'll try again after
898
0
  // reloading our plugin list. Only do that once per document to
899
0
  // avoid redundant high resource usage on pages with multiple
900
0
  // unkown instance types. We'll do that by caching the document.
901
0
  nsCOMPtr<nsIDocument> document;
902
0
  aOwner->GetDocument(getter_AddRefs(document));
903
0
904
0
  nsCOMPtr<nsIDocument> currentdocument = do_QueryReferent(mCurrentDocument);
905
0
  if (document == currentdocument) {
906
0
    return rv;
907
0
  }
908
0
909
0
  mCurrentDocument = do_GetWeakReference(document);
910
0
911
0
  // Don't try to set up an instance again if nothing changed.
912
0
  if (ReloadPlugins() == NS_ERROR_PLUGINS_PLUGINSNOTCHANGED) {
913
0
    return rv;
914
0
  }
915
0
916
0
  return TrySetUpPluginInstance(aMimeType, aURL, aOwner);
917
0
}
918
919
nsresult
920
nsPluginHost::TrySetUpPluginInstance(const nsACString &aMimeType,
921
                                     nsIURI *aURL,
922
                                     nsPluginInstanceOwner *aOwner)
923
0
{
924
#ifdef PLUGIN_LOGGING
925
  MOZ_LOG(nsPluginLogging::gPluginLog, PLUGIN_LOG_NORMAL,
926
          ("nsPluginHost::TrySetupPluginInstance Begin mime=%s, owner=%p, url=%s\n",
927
           PromiseFlatCString(aMimeType).get(), aOwner,
928
           aURL ? aURL->GetSpecOrDefault().get() : ""));
929
930
  PR_LogFlush();
931
#endif
932
933
#ifdef XP_WIN
934
  bool changed;
935
  if ((mRegKeyHKLM && NS_SUCCEEDED(mRegKeyHKLM->HasChanged(&changed)) && changed) ||
936
      (mRegKeyHKCU && NS_SUCCEEDED(mRegKeyHKCU->HasChanged(&changed)) && changed)) {
937
    ReloadPlugins();
938
  }
939
#endif
940
941
0
  RefPtr<nsNPAPIPlugin> plugin;
942
0
  GetPlugin(aMimeType, getter_AddRefs(plugin));
943
0
  if (!plugin) {
944
0
    return NS_ERROR_FAILURE;
945
0
  }
946
0
947
0
  nsPluginTag* pluginTag = FindNativePluginForType(aMimeType, true);
948
0
949
0
  NS_ASSERTION(pluginTag, "Must have plugin tag here!");
950
0
951
0
  plugin->GetLibrary()->SetHasLocalInstance();
952
0
953
0
  RefPtr<nsNPAPIPluginInstance> instance = new nsNPAPIPluginInstance();
954
0
955
0
  // This will create the owning reference. The connection must be made between the
956
0
  // instance and the instance owner before initialization. Plugins can call into
957
0
  // the browser during initialization.
958
0
  aOwner->SetInstance(instance.get());
959
0
960
0
  // Add the instance to the instances list before we call NPP_New so that
961
0
  // it is "in play" before NPP_New happens. Take it out if NPP_New fails.
962
0
  mInstances.AppendElement(instance.get());
963
0
964
0
  // this should not addref the instance or owner
965
0
  // except in some cases not Java, see bug 140931
966
0
  // our COM pointer will free the peer
967
0
  nsresult rv = instance->Initialize(plugin.get(), aOwner, aMimeType);
968
0
  if (NS_FAILED(rv)) {
969
0
    mInstances.RemoveElement(instance.get());
970
0
    aOwner->SetInstance(nullptr);
971
0
    return rv;
972
0
  }
973
0
974
0
  // Cancel the plugin unload timer since we are creating
975
0
  // an instance for it.
976
0
  if (pluginTag->mUnloadTimer) {
977
0
    pluginTag->mUnloadTimer->Cancel();
978
0
  }
979
0
980
#ifdef PLUGIN_LOGGING
981
  MOZ_LOG(nsPluginLogging::gPluginLog, PLUGIN_LOG_BASIC,
982
        ("nsPluginHost::TrySetupPluginInstance Finished mime=%s, rv=%" PRIu32 ", owner=%p, url=%s\n",
983
         PromiseFlatCString(aMimeType).get(), static_cast<uint32_t>(rv), aOwner,
984
         aURL ? aURL->GetSpecOrDefault().get() : ""));
985
986
  PR_LogFlush();
987
#endif
988
989
0
  return rv;
990
0
}
991
992
bool
993
nsPluginHost::HavePluginForType(const nsACString & aMimeType,
994
                                PluginFilter aFilter)
995
0
{
996
0
  bool checkEnabled = aFilter & eExcludeDisabled;
997
0
  bool allowFake = !(aFilter & eExcludeFake);
998
0
  return FindPluginForType(aMimeType, allowFake, checkEnabled);
999
0
}
1000
1001
nsIInternalPluginTag*
1002
nsPluginHost::FindPluginForType(const nsACString& aMimeType,
1003
                                bool aIncludeFake, bool aCheckEnabled)
1004
0
{
1005
0
  if (aIncludeFake) {
1006
0
    nsFakePluginTag* fakeTag = FindFakePluginForType(aMimeType, aCheckEnabled);
1007
0
    if (fakeTag) {
1008
0
      return fakeTag;
1009
0
    }
1010
0
  }
1011
0
1012
0
  return FindNativePluginForType(aMimeType, aCheckEnabled);
1013
0
}
1014
1015
NS_IMETHODIMP
1016
nsPluginHost::GetPluginTagForType(const nsACString& aMimeType,
1017
                                  uint32_t aExcludeFlags,
1018
                                  nsIPluginTag** aResult)
1019
0
{
1020
0
  bool includeFake = !(aExcludeFlags & eExcludeFake);
1021
0
  bool includeDisabled = !(aExcludeFlags & eExcludeDisabled);
1022
0
1023
0
  // First look for an enabled plugin.
1024
0
  RefPtr<nsIInternalPluginTag> tag = FindPluginForType(aMimeType, includeFake,
1025
0
                                                         true);
1026
0
  if (!tag && includeDisabled) {
1027
0
    tag = FindPluginForType(aMimeType, includeFake, false);
1028
0
  }
1029
0
1030
0
  if (tag) {
1031
0
    tag.forget(aResult);
1032
0
    return NS_OK;
1033
0
  }
1034
0
1035
0
  return NS_ERROR_NOT_AVAILABLE;
1036
0
}
1037
1038
NS_IMETHODIMP
1039
nsPluginHost::GetStateForType(const nsACString &aMimeType,
1040
                              uint32_t aExcludeFlags,
1041
                              uint32_t* aResult)
1042
0
{
1043
0
  nsCOMPtr<nsIPluginTag> tag;
1044
0
  nsresult rv = GetPluginTagForType(aMimeType, aExcludeFlags,
1045
0
                                    getter_AddRefs(tag));
1046
0
  NS_ENSURE_SUCCESS(rv, rv);
1047
0
1048
0
  return tag->GetEnabledState(aResult);
1049
0
}
1050
1051
NS_IMETHODIMP
1052
nsPluginHost::GetBlocklistStateForType(const nsACString &aMimeType,
1053
                                       uint32_t aExcludeFlags,
1054
                                       uint32_t *aState)
1055
0
{
1056
0
  nsCOMPtr<nsIPluginTag> tag;
1057
0
  nsresult rv = GetPluginTagForType(aMimeType,
1058
0
                                    aExcludeFlags,
1059
0
                                    getter_AddRefs(tag));
1060
0
  NS_ENSURE_SUCCESS(rv, rv);
1061
0
  return tag->GetBlocklistState(aState);
1062
0
}
1063
1064
NS_IMETHODIMP
1065
nsPluginHost::GetPermissionStringForType(const nsACString &aMimeType,
1066
                                         uint32_t aExcludeFlags,
1067
                                         nsACString &aPermissionString)
1068
0
{
1069
0
  nsCOMPtr<nsIPluginTag> tag;
1070
0
  nsresult rv = GetPluginTagForType(aMimeType, aExcludeFlags,
1071
0
                                    getter_AddRefs(tag));
1072
0
  NS_ENSURE_SUCCESS(rv, rv);
1073
0
  return GetPermissionStringForTag(tag, aExcludeFlags, aPermissionString);
1074
0
}
1075
1076
NS_IMETHODIMP
1077
nsPluginHost::GetPermissionStringForTag(nsIPluginTag* aTag,
1078
                                        uint32_t aExcludeFlags,
1079
                                        nsACString &aPermissionString)
1080
0
{
1081
0
  NS_ENSURE_TRUE(aTag, NS_ERROR_FAILURE);
1082
0
1083
0
  aPermissionString.Truncate();
1084
0
  uint32_t blocklistState;
1085
0
  nsresult rv = aTag->GetBlocklistState(&blocklistState);
1086
0
  NS_ENSURE_SUCCESS(rv, rv);
1087
0
1088
0
  if (blocklistState == nsIBlocklistService::STATE_VULNERABLE_UPDATE_AVAILABLE ||
1089
0
      blocklistState == nsIBlocklistService::STATE_VULNERABLE_NO_UPDATE) {
1090
0
    aPermissionString.AssignLiteral("plugin-vulnerable:");
1091
0
  }
1092
0
  else {
1093
0
    aPermissionString.AssignLiteral("plugin:");
1094
0
  }
1095
0
1096
0
  nsCString niceName;
1097
0
  rv = aTag->GetNiceName(niceName);
1098
0
  NS_ENSURE_SUCCESS(rv, rv);
1099
0
  NS_ENSURE_TRUE(!niceName.IsEmpty(), NS_ERROR_FAILURE);
1100
0
1101
0
  aPermissionString.Append(niceName);
1102
0
1103
0
  return NS_OK;
1104
0
}
1105
1106
bool
1107
nsPluginHost::HavePluginForExtension(const nsACString & aExtension,
1108
                                     /* out */ nsACString & aMimeType,
1109
                                     PluginFilter aFilter)
1110
0
{
1111
0
  // As of FF 52, we only support flash and test plugins, so if the extension types
1112
0
  // don't match for that, exit before we start loading plugins.
1113
0
  //
1114
0
  // XXX: Remove tst case when bug 1351885 lands.
1115
0
  if (!aExtension.LowerCaseEqualsLiteral("swf") &&
1116
0
      !aExtension.LowerCaseEqualsLiteral("tst")) {
1117
0
    return false;
1118
0
  }
1119
0
1120
0
  bool checkEnabled = aFilter & eExcludeDisabled;
1121
0
  bool allowFake = !(aFilter & eExcludeFake);
1122
0
  return FindNativePluginForExtension(aExtension, aMimeType, checkEnabled) ||
1123
0
    (allowFake &&
1124
0
     FindFakePluginForExtension(aExtension, aMimeType, checkEnabled));
1125
0
}
1126
1127
void
1128
nsPluginHost::GetPlugins(nsTArray<nsCOMPtr<nsIInternalPluginTag>>& aPluginArray,
1129
                         bool aIncludeDisabled)
1130
0
{
1131
0
  aPluginArray.Clear();
1132
0
1133
0
  LoadPlugins();
1134
0
1135
0
  // Append fake plugins, then normal plugins.
1136
0
1137
0
  uint32_t numFake = mFakePlugins.Length();
1138
0
  for (uint32_t i = 0; i < numFake; i++) {
1139
0
    aPluginArray.AppendElement(mFakePlugins[i]);
1140
0
  }
1141
0
1142
0
  // Regular plugins
1143
0
  nsPluginTag* plugin = mPlugins;
1144
0
  while (plugin != nullptr) {
1145
0
    if (plugin->IsEnabled() || aIncludeDisabled) {
1146
0
      aPluginArray.AppendElement(plugin);
1147
0
    }
1148
0
    plugin = plugin->mNext;
1149
0
  }
1150
0
}
1151
1152
// FIXME-jsplugins Check users for order of fake v non-fake
1153
NS_IMETHODIMP
1154
nsPluginHost::GetPluginTags(uint32_t* aPluginCount, nsIPluginTag*** aResults)
1155
0
{
1156
0
  LoadPlugins();
1157
0
1158
0
  uint32_t count = 0;
1159
0
  uint32_t fakeCount = mFakePlugins.Length();
1160
0
  RefPtr<nsPluginTag> plugin = mPlugins;
1161
0
  while (plugin != nullptr) {
1162
0
    count++;
1163
0
    plugin = plugin->mNext;
1164
0
  }
1165
0
1166
0
  *aResults = static_cast<nsIPluginTag**>
1167
0
                         (moz_xmalloc((fakeCount + count) * sizeof(**aResults)));
1168
0
1169
0
  *aPluginCount = count + fakeCount;
1170
0
1171
0
  plugin = mPlugins;
1172
0
  for (uint32_t i = 0; i < count; i++) {
1173
0
    (*aResults)[i] = plugin;
1174
0
    NS_ADDREF((*aResults)[i]);
1175
0
    plugin = plugin->mNext;
1176
0
  }
1177
0
1178
0
  for (uint32_t i = 0; i < fakeCount; i++) {
1179
0
    (*aResults)[i + count] = static_cast<nsIInternalPluginTag*>(mFakePlugins[i]);
1180
0
    NS_ADDREF((*aResults)[i + count]);
1181
0
  }
1182
0
1183
0
  return NS_OK;
1184
0
}
1185
1186
nsPluginTag*
1187
nsPluginHost::FindPreferredPlugin(const InfallibleTArray<nsPluginTag*>& matches)
1188
0
{
1189
0
  // We prefer the plugin with the highest version number.
1190
0
  /// XXX(johns): This seems to assume the only time multiple plugins will have
1191
0
  ///             the same MIME type is if they're multiple versions of the same
1192
0
  ///             plugin -- but since plugin filenames and pretty names can both
1193
0
  ///             update, it's probably less arbitrary than just going at it
1194
0
  ///             alphabetically.
1195
0
1196
0
  if (matches.IsEmpty()) {
1197
0
    return nullptr;
1198
0
  }
1199
0
1200
0
  nsPluginTag *preferredPlugin = matches[0];
1201
0
  for (unsigned int i = 1; i < matches.Length(); i++) {
1202
0
    if (mozilla::Version(matches[i]->Version().get()) > preferredPlugin->Version().get()) {
1203
0
      preferredPlugin = matches[i];
1204
0
    }
1205
0
  }
1206
0
1207
0
  return preferredPlugin;
1208
0
}
1209
1210
nsFakePluginTag*
1211
nsPluginHost::FindFakePluginForExtension(const nsACString & aExtension,
1212
                                         /* out */ nsACString & aMimeType,
1213
                                         bool aCheckEnabled)
1214
0
{
1215
0
  if (aExtension.IsEmpty()) {
1216
0
    return nullptr;
1217
0
  }
1218
0
1219
0
  int32_t numFakePlugins = mFakePlugins.Length();
1220
0
  for (int32_t i = 0; i < numFakePlugins; i++) {
1221
0
    nsFakePluginTag *plugin = mFakePlugins[i];
1222
0
    bool active;
1223
0
    if ((!aCheckEnabled ||
1224
0
         (NS_SUCCEEDED(plugin->GetActive(&active)) && active)) &&
1225
0
        plugin->HasExtension(aExtension, aMimeType)) {
1226
0
      return plugin;
1227
0
    }
1228
0
  }
1229
0
1230
0
  return nullptr;
1231
0
}
1232
1233
nsFakePluginTag*
1234
nsPluginHost::FindFakePluginForType(const nsACString & aMimeType,
1235
                                    bool aCheckEnabled)
1236
0
{
1237
0
  int32_t numFakePlugins = mFakePlugins.Length();
1238
0
  for (int32_t i = 0; i < numFakePlugins; i++) {
1239
0
    nsFakePluginTag *plugin = mFakePlugins[i];
1240
0
    bool active;
1241
0
    if ((!aCheckEnabled ||
1242
0
         (NS_SUCCEEDED(plugin->GetActive(&active)) && active)) &&
1243
0
        plugin->HasMimeType(aMimeType)) {
1244
0
      return plugin;
1245
0
    }
1246
0
  }
1247
0
1248
0
  return nullptr;
1249
0
}
1250
1251
nsPluginTag*
1252
nsPluginHost::FindNativePluginForType(const nsACString & aMimeType,
1253
                                      bool aCheckEnabled)
1254
0
{
1255
0
  if (aMimeType.IsEmpty()) {
1256
0
    return nullptr;
1257
0
  }
1258
0
1259
0
  // As of FF 52, we only support flash and test plugins, so if the mime types
1260
0
  // don't match for that, exit before we start loading plugins.
1261
0
  if (!nsPluginHost::CanUsePluginForMIMEType(aMimeType)) {
1262
0
    return nullptr;
1263
0
  }
1264
0
1265
0
  LoadPlugins();
1266
0
1267
0
  InfallibleTArray<nsPluginTag*> matchingPlugins;
1268
0
1269
0
  nsPluginTag *plugin = mPlugins;
1270
0
  while (plugin) {
1271
0
    if ((!aCheckEnabled || plugin->IsActive()) &&
1272
0
        plugin->HasMimeType(aMimeType)) {
1273
0
      matchingPlugins.AppendElement(plugin);
1274
0
    }
1275
0
    plugin = plugin->mNext;
1276
0
  }
1277
0
1278
0
  return FindPreferredPlugin(matchingPlugins);
1279
0
}
1280
1281
nsPluginTag*
1282
nsPluginHost::FindNativePluginForExtension(const nsACString & aExtension,
1283
                                           /* out */ nsACString & aMimeType,
1284
                                           bool aCheckEnabled)
1285
0
{
1286
0
  if (aExtension.IsEmpty()) {
1287
0
    return nullptr;
1288
0
  }
1289
0
1290
0
  LoadPlugins();
1291
0
1292
0
  InfallibleTArray<nsPluginTag*> matchingPlugins;
1293
0
  nsCString matchingMime; // Don't mutate aMimeType unless returning a match
1294
0
  nsPluginTag *plugin = mPlugins;
1295
0
1296
0
  while (plugin) {
1297
0
    if (!aCheckEnabled || plugin->IsActive()) {
1298
0
      if (plugin->HasExtension(aExtension, matchingMime)) {
1299
0
        matchingPlugins.AppendElement(plugin);
1300
0
      }
1301
0
    }
1302
0
    plugin = plugin->mNext;
1303
0
  }
1304
0
1305
0
  nsPluginTag *preferredPlugin = FindPreferredPlugin(matchingPlugins);
1306
0
  if (!preferredPlugin) {
1307
0
    return nullptr;
1308
0
  }
1309
0
1310
0
  // Re-fetch the matching type because of how FindPreferredPlugin works...
1311
0
  preferredPlugin->HasExtension(aExtension, aMimeType);
1312
0
  return preferredPlugin;
1313
0
}
1314
1315
static nsresult CreateNPAPIPlugin(nsPluginTag *aPluginTag,
1316
                                  nsNPAPIPlugin **aOutNPAPIPlugin)
1317
0
{
1318
0
  nsresult rv;
1319
0
  rv = nsNPAPIPlugin::CreatePlugin(aPluginTag, aOutNPAPIPlugin);
1320
0
1321
0
  return rv;
1322
0
}
1323
1324
nsresult nsPluginHost::EnsurePluginLoaded(nsPluginTag* aPluginTag)
1325
0
{
1326
0
  RefPtr<nsNPAPIPlugin> plugin = aPluginTag->mPlugin;
1327
0
  if (!plugin) {
1328
0
    nsresult rv = CreateNPAPIPlugin(aPluginTag, getter_AddRefs(plugin));
1329
0
    if (NS_FAILED(rv)) {
1330
0
      return rv;
1331
0
    }
1332
0
    aPluginTag->mPlugin = plugin;
1333
0
  }
1334
0
  return NS_OK;
1335
0
}
1336
1337
nsresult
1338
nsPluginHost::GetPluginForContentProcess(uint32_t aPluginId, nsNPAPIPlugin** aPlugin)
1339
0
{
1340
0
  AUTO_PROFILER_LABEL("nsPluginHost::GetPluginForContentProcess", OTHER);
1341
0
  MOZ_ASSERT(XRE_IsParentProcess());
1342
0
1343
0
  // If plugins haven't been scanned yet, do so now
1344
0
  LoadPlugins();
1345
0
1346
0
  nsPluginTag* pluginTag = PluginWithId(aPluginId);
1347
0
  if (pluginTag) {
1348
0
    // When setting up a bridge, double check with chrome to see if this plugin
1349
0
    // is blocked hard. Note this does not protect against vulnerable plugins
1350
0
    // that the user has explicitly allowed. :(
1351
0
    if (pluginTag->IsBlocklisted()) {
1352
0
      return NS_ERROR_PLUGIN_BLOCKLISTED;
1353
0
    }
1354
0
1355
0
    nsresult rv = EnsurePluginLoaded(pluginTag);
1356
0
    if (NS_FAILED(rv)) {
1357
0
      return rv;
1358
0
    }
1359
0
1360
0
    // We only get here if a content process doesn't have a PluginModuleParent
1361
0
    // for the given plugin already. Therefore, this counter is counting the
1362
0
    // number of outstanding PluginModuleParents for the plugin, excluding the
1363
0
    // one from the chrome process.
1364
0
    pluginTag->mContentProcessRunningCount++;
1365
0
    NS_ADDREF(*aPlugin = pluginTag->mPlugin);
1366
0
    return NS_OK;
1367
0
  }
1368
0
1369
0
  return NS_ERROR_FAILURE;
1370
0
}
1371
1372
class nsPluginUnloadRunnable : public Runnable
1373
{
1374
public:
1375
  explicit nsPluginUnloadRunnable(uint32_t aPluginId) :
1376
    Runnable("nsPluginUnloadRunnable"),
1377
    mPluginId(aPluginId)
1378
0
  {}
1379
1380
  NS_IMETHOD Run() override
1381
0
  {
1382
0
    RefPtr<nsPluginHost> host = nsPluginHost::GetInst();
1383
0
    if (!host) {
1384
0
      return NS_OK;
1385
0
    }
1386
0
    nsPluginTag* pluginTag = host->PluginWithId(mPluginId);
1387
0
    if (!pluginTag) {
1388
0
      return NS_OK;
1389
0
    }
1390
0
1391
0
    MOZ_ASSERT(pluginTag->mContentProcessRunningCount > 0);
1392
0
    pluginTag->mContentProcessRunningCount--;
1393
0
1394
0
    if (!pluginTag->mContentProcessRunningCount) {
1395
0
      if (!host->IsRunningPlugin(pluginTag)) {
1396
0
        pluginTag->TryUnloadPlugin(false);
1397
0
      }
1398
0
    }
1399
0
    return NS_OK;
1400
0
  }
1401
1402
protected:
1403
  uint32_t mPluginId;
1404
};
1405
1406
void
1407
nsPluginHost::NotifyContentModuleDestroyed(uint32_t aPluginId)
1408
0
{
1409
0
  MOZ_ASSERT(XRE_IsParentProcess());
1410
0
1411
0
  // This is called in response to a message from the plugin. Don't unload the
1412
0
  // plugin until the message handler is off the stack.
1413
0
  RefPtr<nsPluginUnloadRunnable> runnable =
1414
0
    new nsPluginUnloadRunnable(aPluginId);
1415
0
  NS_DispatchToMainThread(runnable);
1416
0
}
1417
1418
nsresult nsPluginHost::GetPlugin(const nsACString &aMimeType,
1419
                                 nsNPAPIPlugin** aPlugin)
1420
0
{
1421
0
  nsresult rv = NS_ERROR_FAILURE;
1422
0
  *aPlugin = nullptr;
1423
0
1424
0
  // If plugins haven't been scanned yet, do so now
1425
0
  LoadPlugins();
1426
0
1427
0
  nsPluginTag* pluginTag = FindNativePluginForType(aMimeType, true);
1428
0
  if (pluginTag) {
1429
0
    rv = NS_OK;
1430
0
    PLUGIN_LOG(PLUGIN_LOG_BASIC,
1431
0
    ("nsPluginHost::GetPlugin Begin mime=%s, plugin=%s\n",
1432
0
     PromiseFlatCString(aMimeType).get(), pluginTag->FileName().get()));
1433
0
1434
#ifdef DEBUG
1435
    if (!pluginTag->FileName().IsEmpty())
1436
      printf("For %s found plugin %s\n",
1437
             PromiseFlatCString(aMimeType).get(), pluginTag->FileName().get());
1438
#endif
1439
1440
0
    rv = EnsurePluginLoaded(pluginTag);
1441
0
    if (NS_FAILED(rv)) {
1442
0
      return rv;
1443
0
    }
1444
0
1445
0
    NS_ADDREF(*aPlugin = pluginTag->mPlugin);
1446
0
    return NS_OK;
1447
0
  }
1448
0
1449
0
  PLUGIN_LOG(PLUGIN_LOG_NORMAL,
1450
0
  ("nsPluginHost::GetPlugin End mime=%s, rv=%" PRIu32 ", plugin=%p name=%s\n",
1451
0
   PromiseFlatCString(aMimeType).get(), static_cast<uint32_t>(rv), *aPlugin,
1452
0
   (pluginTag ? pluginTag->FileName().get() : "(not found)")));
1453
0
1454
0
  return rv;
1455
0
}
1456
1457
// Normalize 'host' to ACE.
1458
nsresult
1459
nsPluginHost::NormalizeHostname(nsCString& host)
1460
0
{
1461
0
  if (IsASCII(host)) {
1462
0
    ToLowerCase(host);
1463
0
    return NS_OK;
1464
0
  }
1465
0
1466
0
  if (!mIDNService) {
1467
0
    nsresult rv;
1468
0
    mIDNService = do_GetService(NS_IDNSERVICE_CONTRACTID, &rv);
1469
0
    NS_ENSURE_SUCCESS(rv, rv);
1470
0
  }
1471
0
1472
0
  return mIDNService->ConvertUTF8toACE(host, host);
1473
0
}
1474
1475
// Enumerate a 'sites' array returned by GetSitesWithData and determine if
1476
// any of them have a base domain in common with 'domain'; if so, append them
1477
// to the 'result' array. If 'firstMatchOnly' is true, return after finding the
1478
// first match.
1479
nsresult
1480
nsPluginHost::EnumerateSiteData(const nsACString& domain,
1481
                                const InfallibleTArray<nsCString>& sites,
1482
                                InfallibleTArray<nsCString>& result,
1483
                                bool firstMatchOnly)
1484
0
{
1485
0
  NS_ASSERTION(!domain.IsVoid(), "null domain string");
1486
0
1487
0
  nsresult rv;
1488
0
  if (!mTLDService) {
1489
0
    mTLDService = do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID, &rv);
1490
0
    NS_ENSURE_SUCCESS(rv, rv);
1491
0
  }
1492
0
1493
0
  // Get the base domain from the domain.
1494
0
  nsCString baseDomain;
1495
0
  rv = mTLDService->GetBaseDomainFromHost(domain, 0, baseDomain);
1496
0
  bool isIP = rv == NS_ERROR_HOST_IS_IP_ADDRESS;
1497
0
  if (isIP || rv == NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS) {
1498
0
    // The base domain is the site itself. However, we must be careful to
1499
0
    // normalize.
1500
0
    baseDomain = domain;
1501
0
    rv = NormalizeHostname(baseDomain);
1502
0
    NS_ENSURE_SUCCESS(rv, rv);
1503
0
  } else if (NS_FAILED(rv)) {
1504
0
    return rv;
1505
0
  }
1506
0
1507
0
  // Enumerate the array of sites with data.
1508
0
  for (uint32_t i = 0; i < sites.Length(); ++i) {
1509
0
    const nsCString& site = sites[i];
1510
0
1511
0
    // Check if the site is an IP address.
1512
0
    bool siteIsIP =
1513
0
      site.Length() >= 2 && site.First() == '[' && site.Last() == ']';
1514
0
    if (siteIsIP != isIP)
1515
0
      continue;
1516
0
1517
0
    nsCString siteBaseDomain;
1518
0
    if (siteIsIP) {
1519
0
      // Strip the '[]'.
1520
0
      siteBaseDomain = Substring(site, 1, site.Length() - 2);
1521
0
    } else {
1522
0
      // Determine the base domain of the site.
1523
0
      rv = mTLDService->GetBaseDomainFromHost(site, 0, siteBaseDomain);
1524
0
      if (rv == NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS) {
1525
0
        // The base domain is the site itself. However, we must be careful to
1526
0
        // normalize.
1527
0
        siteBaseDomain = site;
1528
0
        rv = NormalizeHostname(siteBaseDomain);
1529
0
        NS_ENSURE_SUCCESS(rv, rv);
1530
0
      } else if (NS_FAILED(rv)) {
1531
0
        return rv;
1532
0
      }
1533
0
    }
1534
0
1535
0
    // At this point, we can do an exact comparison of the two domains.
1536
0
    if (baseDomain != siteBaseDomain) {
1537
0
      continue;
1538
0
    }
1539
0
1540
0
    // Append the site to the result array.
1541
0
    result.AppendElement(site);
1542
0
1543
0
    // If we're supposed to return early, do so.
1544
0
    if (firstMatchOnly) {
1545
0
      break;
1546
0
    }
1547
0
  }
1548
0
1549
0
  return NS_OK;
1550
0
}
1551
1552
static bool
1553
MimeTypeIsAllowedForFakePlugin(const nsString& aMimeType)
1554
0
{
1555
0
  static const char* const allowedFakePlugins[] = {
1556
0
    // Flash
1557
0
    "application/x-shockwave-flash",
1558
0
    // PDF
1559
0
    "application/pdf",
1560
0
    "application/vnd.adobe.pdf",
1561
0
    "application/vnd.adobe.pdfxml",
1562
0
    "application/vnd.adobe.x-mars",
1563
0
    "application/vnd.adobe.xdp+xml",
1564
0
    "application/vnd.adobe.xfdf",
1565
0
    "application/vnd.adobe.xfd+xml",
1566
0
    "application/vnd.fdf",
1567
0
  };
1568
0
1569
0
  for (const auto allowed : allowedFakePlugins) {
1570
0
    if (aMimeType.EqualsASCII(allowed)) {
1571
0
      return true;
1572
0
    }
1573
0
  }
1574
0
  return false;
1575
0
}
1576
1577
NS_IMETHODIMP
1578
nsPluginHost::RegisterFakePlugin(JS::Handle<JS::Value> aInitDictionary,
1579
                                 JSContext* aCx,
1580
                                 nsIFakePluginTag **aResult)
1581
0
{
1582
0
  FakePluginTagInit initDictionary;
1583
0
  if (!initDictionary.Init(aCx, aInitDictionary)) {
1584
0
    return NS_ERROR_FAILURE;
1585
0
  }
1586
0
1587
0
  for (const FakePluginMimeEntry& mimeEntry : initDictionary.mMimeEntries) {
1588
0
    if (!MimeTypeIsAllowedForFakePlugin(mimeEntry.mType)) {
1589
0
      return NS_ERROR_FAILURE;
1590
0
    }
1591
0
  }
1592
0
1593
0
  RefPtr<nsFakePluginTag> newTag;
1594
0
  nsresult rv = nsFakePluginTag::Create(initDictionary, getter_AddRefs(newTag));
1595
0
  NS_ENSURE_SUCCESS(rv, rv);
1596
0
1597
0
  for (const auto& existingTag : mFakePlugins) {
1598
0
    if (newTag->HandlerURIMatches(existingTag->HandlerURI())) {
1599
0
      return NS_ERROR_UNEXPECTED;
1600
0
    }
1601
0
  }
1602
0
1603
0
  mFakePlugins.AppendElement(newTag);
1604
0
1605
0
  nsAutoCString disableFullPage;
1606
0
  Preferences::GetCString(kPrefDisableFullPage, disableFullPage);
1607
0
  for (uint32_t i = 0; i < newTag->MimeTypes().Length(); i++) {
1608
0
    if (!IsTypeInList(newTag->MimeTypes()[i], disableFullPage)) {
1609
0
      RegisterWithCategoryManager(newTag->MimeTypes()[i],
1610
0
                                  ePluginRegister);
1611
0
    }
1612
0
  }
1613
0
1614
0
  newTag.forget(aResult);
1615
0
  return NS_OK;
1616
0
}
1617
1618
NS_IMETHODIMP
1619
nsPluginHost::CreateFakePlugin(JS::Handle<JS::Value> aInitDictionary,
1620
                               JSContext* aCx,
1621
                               nsIFakePluginTag **aResult)
1622
0
{
1623
0
  FakePluginTagInit initDictionary;
1624
0
  if (!initDictionary.Init(aCx, aInitDictionary)) {
1625
0
    return NS_ERROR_FAILURE;
1626
0
  }
1627
0
1628
0
  RefPtr<nsFakePluginTag> newTag;
1629
0
  nsresult rv = nsFakePluginTag::Create(initDictionary, getter_AddRefs(newTag));
1630
0
  NS_ENSURE_SUCCESS(rv, rv);
1631
0
1632
0
  newTag.forget(aResult);
1633
0
  return NS_OK;
1634
0
}
1635
1636
NS_IMETHODIMP
1637
nsPluginHost::UnregisterFakePlugin(const nsACString& aHandlerURI)
1638
0
{
1639
0
  nsCOMPtr<nsIURI> handlerURI;
1640
0
  nsresult rv = NS_NewURI(getter_AddRefs(handlerURI), aHandlerURI);
1641
0
  NS_ENSURE_SUCCESS(rv, rv);
1642
0
1643
0
  for (uint32_t i = 0; i < mFakePlugins.Length(); ++i) {
1644
0
    if (mFakePlugins[i]->HandlerURIMatches(handlerURI)) {
1645
0
      mFakePlugins.RemoveElementAt(i);
1646
0
      return NS_OK;
1647
0
    }
1648
0
  }
1649
0
1650
0
  return NS_OK;
1651
0
}
1652
1653
// FIXME-jsplugins Is this method actually needed?
1654
NS_IMETHODIMP
1655
nsPluginHost::GetFakePlugin(const nsACString & aMimeType,
1656
                            nsIFakePluginTag** aResult)
1657
0
{
1658
0
  RefPtr<nsFakePluginTag> result = FindFakePluginForType(aMimeType, false);
1659
0
  if (result) {
1660
0
    result.forget(aResult);
1661
0
    return NS_OK;
1662
0
  }
1663
0
1664
0
  *aResult = nullptr;
1665
0
  return NS_ERROR_NOT_AVAILABLE;
1666
0
}
1667
1668
#define ClearDataFromSitesClosure_CID {0x9fb21761, 0x2403, 0x41ad, {0x9e, 0xfd, 0x36, 0x7e, 0xc4, 0x4f, 0xa4, 0x5e}}
1669
1670
1671
// Class to hold all the data we need need for IterateMatchesAndClear and ClearDataFromSites
1672
class ClearDataFromSitesClosure : public nsIClearSiteDataCallback, public nsIGetSitesWithDataCallback {
1673
public:
1674
  ClearDataFromSitesClosure(nsIPluginTag* plugin, const nsACString& domain, uint64_t flags,
1675
                            int64_t maxAge, nsCOMPtr<nsIClearSiteDataCallback> callback,
1676
                            nsPluginHost* host) :
1677
0
    domain(domain), callback(callback), tag(plugin), flags(flags), maxAge(maxAge), host(host) {}
1678
  NS_DECL_ISUPPORTS
1679
1680
  // Callback from NPP_ClearSiteData, continue to iterate the matches and clear
1681
0
  NS_IMETHOD Callback(nsresult rv) override {
1682
0
    if (NS_FAILED(rv)) {
1683
0
      callback->Callback(rv);
1684
0
      return NS_OK;
1685
0
    }
1686
0
    if (!matches.Length()) {
1687
0
      callback->Callback(NS_OK);
1688
0
      return NS_OK;
1689
0
    }
1690
0
1691
0
    const nsCString match(matches[0]);
1692
0
    matches.RemoveElement(match);
1693
0
    PluginLibrary* library = static_cast<nsPluginTag*>(tag)->mPlugin->GetLibrary();
1694
0
    rv = library->NPP_ClearSiteData(match.get(), flags, maxAge, this);
1695
0
    if (NS_FAILED(rv)) {
1696
0
      callback->Callback(rv);
1697
0
      return NS_OK;
1698
0
    }
1699
0
    return NS_OK;
1700
0
  }
1701
1702
  // Callback from NPP_GetSitesWithData, kick the iteration off to clear the data
1703
  NS_IMETHOD SitesWithData(InfallibleTArray<nsCString>& sites) override
1704
0
  {
1705
0
    // Enumerate the sites and build a list of matches.
1706
0
    nsresult rv = host->EnumerateSiteData(domain, sites, matches, false);
1707
0
    Callback(rv);
1708
0
    return NS_OK;
1709
0
  }
1710
1711
  nsCString domain;
1712
  nsCOMPtr<nsIClearSiteDataCallback> callback;
1713
  InfallibleTArray<nsCString> matches;
1714
  nsIPluginTag* tag;
1715
  uint64_t flags;
1716
  int64_t maxAge;
1717
  nsPluginHost* host;
1718
  NS_DECLARE_STATIC_IID_ACCESSOR(ClearDataFromSitesClosure_CID)
1719
  private:
1720
0
  virtual ~ClearDataFromSitesClosure() = default;
1721
};
1722
1723
NS_DEFINE_STATIC_IID_ACCESSOR(ClearDataFromSitesClosure, ClearDataFromSitesClosure_CID)
1724
1725
NS_IMPL_ADDREF(ClearDataFromSitesClosure)
1726
NS_IMPL_RELEASE(ClearDataFromSitesClosure)
1727
1728
0
NS_INTERFACE_MAP_BEGIN(ClearDataFromSitesClosure)
1729
0
  NS_INTERFACE_MAP_ENTRY(nsIClearSiteDataCallback)
1730
0
  NS_INTERFACE_MAP_ENTRY(nsIGetSitesWithDataCallback)
1731
0
  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIClearSiteDataCallback)
1732
0
NS_INTERFACE_MAP_END
1733
1734
// FIXME-jsplugins what should this do for fake plugins?
1735
NS_IMETHODIMP
1736
nsPluginHost::ClearSiteData(nsIPluginTag* plugin, const nsACString& domain,
1737
                            uint64_t flags, int64_t maxAge, nsIClearSiteDataCallback* callbackFunc)
1738
0
{
1739
0
  nsCOMPtr<nsIClearSiteDataCallback> callback(callbackFunc);
1740
0
  // maxAge must be either a nonnegative integer or -1.
1741
0
  NS_ENSURE_ARG(maxAge >= 0 || maxAge == -1);
1742
0
1743
0
  // Caller may give us a tag object that is no longer live.
1744
0
  if (!IsLiveTag(plugin)) {
1745
0
    return NS_ERROR_NOT_AVAILABLE;
1746
0
  }
1747
0
1748
0
  nsPluginTag* tag = static_cast<nsPluginTag*>(plugin);
1749
0
1750
0
  if (!tag->IsEnabled()) {
1751
0
    return NS_ERROR_NOT_AVAILABLE;
1752
0
  }
1753
0
1754
0
  // We only ensure support for clearing Flash site data for now.
1755
0
  // We will also attempt to clear data for any plugin that happens
1756
0
  // to be loaded already.
1757
0
  if (!tag->mIsFlashPlugin && !tag->mPlugin) {
1758
0
    return NS_ERROR_FAILURE;
1759
0
  }
1760
0
1761
0
  // Make sure the plugin is loaded.
1762
0
  nsresult rv = EnsurePluginLoaded(tag);
1763
0
  if (NS_FAILED(rv)) {
1764
0
    return rv;
1765
0
  }
1766
0
1767
0
  PluginLibrary* library = tag->mPlugin->GetLibrary();
1768
0
1769
0
  // If 'domain' is the null string, clear everything.
1770
0
  if (domain.IsVoid()) {
1771
0
    return library->NPP_ClearSiteData(nullptr, flags, maxAge, callback);
1772
0
  }
1773
0
  nsCOMPtr<nsIGetSitesWithDataCallback> closure(new ClearDataFromSitesClosure(plugin, domain, flags,
1774
0
                                                                              maxAge, callback, this));
1775
0
  rv = library->NPP_GetSitesWithData(closure);
1776
0
  NS_ENSURE_SUCCESS(rv, rv);
1777
0
  return NS_OK;
1778
0
}
1779
1780
#define GetSitesClosure_CID {0x4c9268ac, 0x2fd1, 0x4f2a, {0x9a, 0x10, 0x7a, 0x09, 0xf1, 0xb7, 0x60, 0x3a}}
1781
1782
// Closure to contain the data needed to handle the callback from NPP_GetSitesWithData
1783
class GetSitesClosure : public nsIGetSitesWithDataCallback {
1784
public:
1785
  NS_DECL_ISUPPORTS
1786
  GetSitesClosure(const nsACString& domain, nsPluginHost* host)
1787
    : domain(domain)
1788
    , host(host)
1789
    , result{ false }
1790
    , keepWaiting(true)
1791
    , retVal(NS_ERROR_NOT_INITIALIZED)
1792
0
  {
1793
0
  }
1794
1795
0
  NS_IMETHOD SitesWithData(InfallibleTArray<nsCString>& sites) override {
1796
0
    retVal = HandleGetSites(sites);
1797
0
    keepWaiting = false;
1798
0
    return NS_OK;
1799
0
  }
1800
1801
0
  nsresult HandleGetSites(InfallibleTArray<nsCString>& sites) {
1802
0
    // If there's no data, we're done.
1803
0
    if (sites.IsEmpty()) {
1804
0
      result = false;
1805
0
      return NS_OK;
1806
0
    }
1807
0
1808
0
    // If 'domain' is the null string, and there's data for at least one site,
1809
0
    // we're done.
1810
0
    if (domain.IsVoid()) {
1811
0
      result = true;
1812
0
      return NS_OK;
1813
0
    }
1814
0
1815
0
    // Enumerate the sites and determine if there's a match.
1816
0
    InfallibleTArray<nsCString> matches;
1817
0
    nsresult rv = host->EnumerateSiteData(domain, sites, matches, true);
1818
0
    NS_ENSURE_SUCCESS(rv, rv);
1819
0
1820
0
    result = !matches.IsEmpty();
1821
0
    return NS_OK;
1822
0
  }
1823
1824
  nsCString domain;
1825
  RefPtr<nsPluginHost> host;
1826
  bool result;
1827
  bool keepWaiting;
1828
  nsresult retVal;
1829
  NS_DECLARE_STATIC_IID_ACCESSOR(GetSitesClosure_CID)
1830
  private:
1831
0
  virtual ~GetSitesClosure() = default;
1832
};
1833
1834
NS_DEFINE_STATIC_IID_ACCESSOR(GetSitesClosure, GetSitesClosure_CID)
1835
1836
NS_IMPL_ISUPPORTS(GetSitesClosure, GetSitesClosure, nsIGetSitesWithDataCallback)
1837
1838
// This will spin the event loop while waiting on an async
1839
// call to GetSitesWithData
1840
NS_IMETHODIMP
1841
nsPluginHost::SiteHasData(nsIPluginTag* plugin, const nsACString& domain,
1842
                          bool* result)
1843
0
{
1844
0
  // Caller may give us a tag object that is no longer live.
1845
0
  if (!IsLiveTag(plugin)) {
1846
0
    return NS_ERROR_NOT_AVAILABLE;
1847
0
  }
1848
0
1849
0
  // FIXME-jsplugins audit casts
1850
0
  nsPluginTag* tag = static_cast<nsPluginTag*>(plugin);
1851
0
1852
0
  // We only ensure support for clearing Flash site data for now.
1853
0
  // We will also attempt to clear data for any plugin that happens
1854
0
  // to be loaded already.
1855
0
  if (!tag->mIsFlashPlugin && !tag->mPlugin) {
1856
0
    return NS_ERROR_FAILURE;
1857
0
  }
1858
0
1859
0
  // Make sure the plugin is loaded.
1860
0
  nsresult rv = EnsurePluginLoaded(tag);
1861
0
  if (NS_FAILED(rv)) {
1862
0
    return rv;
1863
0
  }
1864
0
1865
0
  PluginLibrary* library = tag->mPlugin->GetLibrary();
1866
0
1867
0
  // Get the list of sites from the plugin
1868
0
  nsCOMPtr<GetSitesClosure> closure(new GetSitesClosure(domain, this));
1869
0
  rv = library->NPP_GetSitesWithData(nsCOMPtr<nsIGetSitesWithDataCallback>(do_QueryInterface(closure)));
1870
0
  NS_ENSURE_SUCCESS(rv, rv);
1871
0
  // Spin the event loop while we wait for the async call to GetSitesWithData
1872
0
  SpinEventLoopUntil([&]() { return !closure->keepWaiting; });
1873
0
  *result = closure->result;
1874
0
  return closure->retVal;
1875
0
}
1876
1877
nsPluginHost::SpecialType
1878
nsPluginHost::GetSpecialType(const nsACString & aMIMEType)
1879
0
{
1880
0
  if (aMIMEType.LowerCaseEqualsASCII("application/x-test")) {
1881
0
    return eSpecialType_Test;
1882
0
  }
1883
0
1884
0
  if (aMIMEType.LowerCaseEqualsASCII("application/x-shockwave-flash") ||
1885
0
      aMIMEType.LowerCaseEqualsASCII("application/futuresplash") ||
1886
0
      aMIMEType.LowerCaseEqualsASCII("application/x-shockwave-flash-test")) {
1887
0
    return eSpecialType_Flash;
1888
0
  }
1889
0
1890
0
  return eSpecialType_None;
1891
0
}
1892
1893
// Check whether or not a tag is a live, valid tag, and that it's loaded.
1894
bool
1895
nsPluginHost::IsLiveTag(nsIPluginTag* aPluginTag)
1896
0
{
1897
0
  nsCOMPtr<nsIInternalPluginTag> internalTag(do_QueryInterface(aPluginTag));
1898
0
  uint32_t fakeCount = mFakePlugins.Length();
1899
0
  for (uint32_t i = 0; i < fakeCount; i++) {
1900
0
    if (mFakePlugins[i] == internalTag) {
1901
0
      return true;
1902
0
    }
1903
0
  }
1904
0
1905
0
  nsPluginTag* tag;
1906
0
  for (tag = mPlugins; tag; tag = tag->mNext) {
1907
0
    if (tag == internalTag) {
1908
0
      return true;
1909
0
    }
1910
0
  }
1911
0
  return false;
1912
0
}
1913
1914
// FIXME-jsplugins what should happen with jsplugins here, if anything?
1915
nsPluginTag*
1916
nsPluginHost::HaveSamePlugin(const nsPluginTag* aPluginTag)
1917
0
{
1918
0
  for (nsPluginTag* tag = mPlugins; tag; tag = tag->mNext) {
1919
0
    if (tag->HasSameNameAndMimes(aPluginTag)) {
1920
0
        return tag;
1921
0
    }
1922
0
  }
1923
0
  return nullptr;
1924
0
}
1925
1926
// Don't have to worry about fake plugins here, since this is only used during
1927
// the plugin directory scan, which doesn't pick up fake plugins.
1928
nsPluginTag*
1929
nsPluginHost::FirstPluginWithPath(const nsCString& path)
1930
0
{
1931
0
  for (nsPluginTag* tag = mPlugins; tag; tag = tag->mNext) {
1932
0
    if (tag->mFullPath.Equals(path)) {
1933
0
      return tag;
1934
0
    }
1935
0
  }
1936
0
  return nullptr;
1937
0
}
1938
1939
nsPluginTag*
1940
nsPluginHost::PluginWithId(uint32_t aId)
1941
0
{
1942
0
  for (nsPluginTag* tag = mPlugins; tag; tag = tag->mNext) {
1943
0
    if (tag->mId == aId) {
1944
0
      return tag;
1945
0
    }
1946
0
  }
1947
0
  return nullptr;
1948
0
}
1949
1950
namespace {
1951
1952
int64_t GetPluginLastModifiedTime(const nsCOMPtr<nsIFile>& localfile)
1953
0
{
1954
0
  PRTime fileModTime = 0;
1955
0
1956
#if defined(XP_MACOSX)
1957
  // On OS X the date of a bundle's "contents" (i.e. of its Info.plist file)
1958
  // is a much better guide to when it was last modified than the date of
1959
  // its package directory.  See bug 313700.
1960
  nsCOMPtr<nsILocalFileMac> localFileMac = do_QueryInterface(localfile);
1961
  if (localFileMac) {
1962
    localFileMac->GetBundleContentsLastModifiedTime(&fileModTime);
1963
  } else {
1964
    localfile->GetLastModifiedTime(&fileModTime);
1965
  }
1966
#else
1967
  localfile->GetLastModifiedTime(&fileModTime);
1968
0
#endif
1969
0
1970
0
  return fileModTime;
1971
0
}
1972
1973
bool
1974
GetPluginIsFromExtension(const nsCOMPtr<nsIFile>& pluginFile,
1975
                         const nsCOMArray<nsIFile>& extensionDirs)
1976
0
{
1977
0
  for (uint32_t i = 0; i < extensionDirs.Length(); ++i) {
1978
0
    bool contains;
1979
0
    if (NS_FAILED(extensionDirs[i]->Contains(pluginFile, &contains)) || !contains) {
1980
0
      continue;
1981
0
    }
1982
0
1983
0
    return true;
1984
0
  }
1985
0
1986
0
  return false;
1987
0
}
1988
1989
void
1990
GetExtensionDirectories(nsCOMArray<nsIFile>& dirs)
1991
0
{
1992
0
  nsCOMPtr<nsIProperties> dirService = do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID);
1993
0
  if (!dirService) {
1994
0
    return;
1995
0
  }
1996
0
1997
0
  nsCOMPtr<nsISimpleEnumerator> list;
1998
0
  nsresult rv = dirService->Get(XRE_EXTENSIONS_DIR_LIST,
1999
0
                                NS_GET_IID(nsISimpleEnumerator),
2000
0
                                getter_AddRefs(list));
2001
0
  if (NS_FAILED(rv)) {
2002
0
    return;
2003
0
  }
2004
0
2005
0
  bool more;
2006
0
  while (NS_SUCCEEDED(list->HasMoreElements(&more)) && more) {
2007
0
    nsCOMPtr<nsISupports> next;
2008
0
    if (NS_FAILED(list->GetNext(getter_AddRefs(next)))) {
2009
0
      break;
2010
0
    }
2011
0
    nsCOMPtr<nsIFile> file = do_QueryInterface(next);
2012
0
    if (file) {
2013
0
      file->Normalize();
2014
0
      dirs.AppendElement(file.forget());
2015
0
    }
2016
0
  }
2017
0
}
2018
2019
struct CompareFilesByTime
2020
{
2021
  bool
2022
  LessThan(const nsCOMPtr<nsIFile>& a, const nsCOMPtr<nsIFile>& b) const
2023
0
  {
2024
0
    return GetPluginLastModifiedTime(a) < GetPluginLastModifiedTime(b);
2025
0
  }
2026
2027
  bool
2028
  Equals(const nsCOMPtr<nsIFile>& a, const nsCOMPtr<nsIFile>& b) const
2029
0
  {
2030
0
    return GetPluginLastModifiedTime(a) == GetPluginLastModifiedTime(b);
2031
0
  }
2032
};
2033
2034
} // namespace
2035
2036
static
2037
bool
2038
ShouldAddPlugin(const nsPluginInfo& info, bool flashOnly)
2039
0
{
2040
0
  if (!info.fName || (strcmp(info.fName, "Shockwave Flash") != 0 && flashOnly)) {
2041
0
    return false;
2042
0
  }
2043
0
  for (uint32_t i = 0; i < info.fVariantCount; ++i) {
2044
0
    if (info.fMimeTypeArray[i] &&
2045
0
        (!strcmp(info.fMimeTypeArray[i], "application/x-shockwave-flash") ||
2046
0
         !strcmp(info.fMimeTypeArray[i], "application/x-shockwave-flash-test"))) {
2047
0
      return true;
2048
0
    }
2049
0
    if (flashOnly) {
2050
0
      continue;
2051
0
    }
2052
0
    if (info.fMimeTypeArray[i] &&
2053
0
        (!strcmp(info.fMimeTypeArray[i], "application/x-test") ||
2054
0
         !strcmp(info.fMimeTypeArray[i], "application/x-Second-Test"))) {
2055
0
      return true;
2056
0
    }
2057
0
  }
2058
#ifdef PLUGIN_LOGGING
2059
  PLUGIN_LOG(PLUGIN_LOG_NORMAL,
2060
             ("ShouldAddPlugin : Ignoring non-flash plugin library %s\n", aPluginTag->FileName().get()));
2061
#endif // PLUGIN_LOGGING
2062
0
  return false;
2063
0
}
2064
2065
void
2066
nsPluginHost::AddPluginTag(nsPluginTag* aPluginTag)
2067
0
{
2068
0
  aPluginTag->mNext = mPlugins;
2069
0
  mPlugins = aPluginTag;
2070
0
2071
0
  if (aPluginTag->IsActive()) {
2072
0
    nsAutoCString disableFullPage;
2073
0
    Preferences::GetCString(kPrefDisableFullPage, disableFullPage);
2074
0
    for (uint32_t i = 0; i < aPluginTag->MimeTypes().Length(); i++) {
2075
0
      if (!IsTypeInList(aPluginTag->MimeTypes()[i], disableFullPage)) {
2076
0
        RegisterWithCategoryManager(aPluginTag->MimeTypes()[i],
2077
0
                                    ePluginRegister);
2078
0
      }
2079
0
    }
2080
0
  }
2081
0
}
2082
2083
typedef NS_NPAPIPLUGIN_CALLBACK(char *, NP_GETMIMEDESCRIPTION)(void);
2084
2085
nsresult nsPluginHost::ScanPluginsDirectory(nsIFile *pluginsDir,
2086
                                            bool aCreatePluginList,
2087
                                            bool *aPluginsChanged)
2088
0
{
2089
0
  MOZ_ASSERT(XRE_IsParentProcess());
2090
0
2091
0
  NS_ENSURE_ARG_POINTER(aPluginsChanged);
2092
0
  nsresult rv;
2093
0
2094
0
  *aPluginsChanged = false;
2095
0
2096
#ifdef PLUGIN_LOGGING
2097
  nsAutoCString dirPath;
2098
  pluginsDir->GetNativePath(dirPath);
2099
  PLUGIN_LOG(PLUGIN_LOG_BASIC,
2100
  ("nsPluginHost::ScanPluginsDirectory dir=%s\n", dirPath.get()));
2101
#endif
2102
2103
0
  bool flashOnly = Preferences::GetBool("plugin.load_flash_only", true);
2104
0
2105
0
  nsCOMPtr<nsIDirectoryEnumerator> iter;
2106
0
  rv = pluginsDir->GetDirectoryEntries(getter_AddRefs(iter));
2107
0
  if (NS_FAILED(rv))
2108
0
    return rv;
2109
0
2110
0
  AutoTArray<nsCOMPtr<nsIFile>, 6> pluginFiles;
2111
0
2112
0
  nsCOMPtr<nsIFile> dirEntry;
2113
0
  while (NS_SUCCEEDED(iter->GetNextFile(getter_AddRefs(dirEntry))) && dirEntry) {
2114
0
    // Sun's JRE 1.3.1 plugin must have symbolic links resolved or else it'll crash.
2115
0
    // See bug 197855.
2116
0
    dirEntry->Normalize();
2117
0
2118
0
    if (nsPluginsDir::IsPluginFile(dirEntry)) {
2119
0
      pluginFiles.AppendElement(dirEntry);
2120
0
    }
2121
0
  }
2122
0
2123
0
  pluginFiles.Sort(CompareFilesByTime());
2124
0
2125
0
  nsCOMArray<nsIFile> extensionDirs;
2126
0
  GetExtensionDirectories(extensionDirs);
2127
0
2128
0
  for (int32_t i = (pluginFiles.Length() - 1); i >= 0; i--) {
2129
0
    nsCOMPtr<nsIFile>& localfile = pluginFiles[i];
2130
0
2131
0
    nsString utf16FilePath;
2132
0
    rv = localfile->GetPath(utf16FilePath);
2133
0
    if (NS_FAILED(rv))
2134
0
      continue;
2135
0
2136
0
    const int64_t fileModTime = GetPluginLastModifiedTime(localfile);
2137
0
    const bool fromExtension = GetPluginIsFromExtension(localfile, extensionDirs);
2138
0
2139
0
    // Look for it in our cache
2140
0
    NS_ConvertUTF16toUTF8 filePath(utf16FilePath);
2141
0
    RefPtr<nsPluginTag> pluginTag;
2142
0
    RemoveCachedPluginsInfo(filePath.get(), getter_AddRefs(pluginTag));
2143
0
2144
0
    bool seenBefore = false;
2145
0
    uint32_t blocklistState = nsIBlocklistService::STATE_NOT_BLOCKED;
2146
0
2147
0
    if (pluginTag) {
2148
0
      seenBefore = true;
2149
0
      blocklistState = pluginTag->GetBlocklistState();
2150
0
      // If plugin changed, delete cachedPluginTag and don't use cache
2151
0
      if (fileModTime != pluginTag->mLastModifiedTime) {
2152
0
        // Plugins has changed. Don't use cached plugin info.
2153
0
        pluginTag = nullptr;
2154
0
2155
0
        // plugin file changed, flag this fact
2156
0
        *aPluginsChanged = true;
2157
0
      }
2158
0
2159
0
      // If we're not creating a list and we already know something changed then
2160
0
      // we're done.
2161
0
      if (!aCreatePluginList) {
2162
0
        if (*aPluginsChanged) {
2163
0
          return NS_OK;
2164
0
        }
2165
0
        continue;
2166
0
      }
2167
0
    }
2168
0
2169
0
    bool isKnownInvalidPlugin = false;
2170
0
    for (RefPtr<nsInvalidPluginTag> invalidPlugins = mInvalidPlugins;
2171
0
         invalidPlugins; invalidPlugins = invalidPlugins->mNext) {
2172
0
      // If already marked as invalid, ignore it
2173
0
      if (invalidPlugins->mFullPath.Equals(filePath.get()) &&
2174
0
          invalidPlugins->mLastModifiedTime == fileModTime) {
2175
0
        if (aCreatePluginList) {
2176
0
          invalidPlugins->mSeen = true;
2177
0
        }
2178
0
        isKnownInvalidPlugin = true;
2179
0
        break;
2180
0
      }
2181
0
    }
2182
0
    if (isKnownInvalidPlugin) {
2183
0
      continue;
2184
0
    }
2185
0
2186
0
    // if it is not found in cache info list or has been changed, create a new one
2187
0
    if (!pluginTag) {
2188
0
      nsPluginFile pluginFile(localfile);
2189
0
2190
0
      // create a tag describing this plugin.
2191
0
      PRLibrary *library = nullptr;
2192
0
      nsPluginInfo info;
2193
0
      memset(&info, 0, sizeof(info));
2194
0
      nsresult res;
2195
0
      // Opening a block for the telemetry AutoTimer
2196
0
      {
2197
0
        Telemetry::AutoTimer<Telemetry::PLUGIN_LOAD_METADATA> telemetry;
2198
0
        res = pluginFile.GetPluginInfo(info, &library);
2199
0
      }
2200
0
      // if we don't have mime type don't proceed, this is not a plugin
2201
0
      if (NS_FAILED(res) || !info.fMimeTypeArray ||
2202
0
          (!ShouldAddPlugin(info, flashOnly))) {
2203
0
        RefPtr<nsInvalidPluginTag> invalidTag = new nsInvalidPluginTag(filePath.get(),
2204
0
                                                                         fileModTime);
2205
0
        pluginFile.FreePluginInfo(info);
2206
0
2207
0
        if (aCreatePluginList) {
2208
0
          invalidTag->mSeen = true;
2209
0
        }
2210
0
        invalidTag->mNext = mInvalidPlugins;
2211
0
        if (mInvalidPlugins) {
2212
0
          mInvalidPlugins->mPrev = invalidTag;
2213
0
        }
2214
0
        mInvalidPlugins = invalidTag;
2215
0
2216
0
        // Mark aPluginsChanged so pluginreg is rewritten
2217
0
        *aPluginsChanged = true;
2218
0
        continue;
2219
0
      }
2220
0
2221
0
      pluginTag = new nsPluginTag(&info, fileModTime, fromExtension, blocklistState);
2222
0
      pluginTag->mLibrary = library;
2223
0
      pluginFile.FreePluginInfo(info);
2224
0
      // Pass whether we've seen this plugin before. If the plugin is
2225
0
      // softblocked and new (not seen before), it will be disabled.
2226
0
      UpdatePluginBlocklistState(pluginTag, !seenBefore);
2227
0
2228
0
      // Plugin unloading is tag-based. If we created a new tag and loaded
2229
0
      // the library in the process then we want to attempt to unload it here.
2230
0
      // Only do this if the pref is set for aggressive unloading.
2231
0
      if (UnloadPluginsASAP()) {
2232
0
        pluginTag->TryUnloadPlugin(false);
2233
0
      }
2234
0
    }
2235
0
2236
0
    // do it if we still want it
2237
0
    if (!seenBefore) {
2238
0
      // We have a valid new plugin so report that plugins have changed.
2239
0
      *aPluginsChanged = true;
2240
0
    }
2241
0
2242
0
    // Don't add the same plugin again if it hasn't changed
2243
0
    if (nsPluginTag* duplicate = FirstPluginWithPath(pluginTag->mFullPath)) {
2244
0
      if (pluginTag->mLastModifiedTime == duplicate->mLastModifiedTime) {
2245
0
        continue;
2246
0
      }
2247
0
    }
2248
0
2249
0
    // If we're not creating a plugin list, simply looking for changes,
2250
0
    // then we're done.
2251
0
    if (!aCreatePluginList) {
2252
0
      return NS_OK;
2253
0
    }
2254
0
2255
0
    AddPluginTag(pluginTag);
2256
0
  }
2257
0
2258
0
  return NS_OK;
2259
0
}
2260
2261
void
2262
nsPluginHost::UpdatePluginBlocklistState(nsPluginTag* aPluginTag, bool aShouldSoftblock)
2263
0
{
2264
0
  nsCOMPtr<nsIBlocklistService> blocklist =
2265
0
    do_GetService("@mozilla.org/extensions/blocklist;1");
2266
0
  MOZ_ASSERT(blocklist, "Should be able to access the blocklist");
2267
0
  if (!blocklist) {
2268
0
    return;
2269
0
  }
2270
0
  // Asynchronously get the blocklist state.
2271
0
  RefPtr<Promise> promise;
2272
0
  blocklist->GetPluginBlocklistState(aPluginTag, EmptyString(),
2273
0
                                     EmptyString(), getter_AddRefs(promise));
2274
0
  MOZ_ASSERT(promise, "Should always get a promise for plugin blocklist state.");
2275
0
  if (promise) {
2276
0
    promise->AppendNativeHandler(new mozilla::plugins::BlocklistPromiseHandler(aPluginTag, aShouldSoftblock));
2277
0
  }
2278
0
}
2279
2280
nsresult nsPluginHost::ScanPluginsDirectoryList(nsISimpleEnumerator *dirEnum,
2281
                                                bool aCreatePluginList,
2282
                                                bool *aPluginsChanged)
2283
0
{
2284
0
  MOZ_ASSERT(XRE_IsParentProcess());
2285
0
2286
0
    bool hasMore;
2287
0
    while (NS_SUCCEEDED(dirEnum->HasMoreElements(&hasMore)) && hasMore) {
2288
0
      nsCOMPtr<nsISupports> supports;
2289
0
      nsresult rv = dirEnum->GetNext(getter_AddRefs(supports));
2290
0
      if (NS_FAILED(rv))
2291
0
        continue;
2292
0
      nsCOMPtr<nsIFile> nextDir(do_QueryInterface(supports, &rv));
2293
0
      if (NS_FAILED(rv))
2294
0
        continue;
2295
0
2296
0
      // don't pass aPluginsChanged directly to prevent it from been reset
2297
0
      bool pluginschanged = false;
2298
0
      ScanPluginsDirectory(nextDir, aCreatePluginList, &pluginschanged);
2299
0
2300
0
      if (pluginschanged)
2301
0
        *aPluginsChanged = true;
2302
0
2303
0
      // if changes are detected and we are not creating the list, do not proceed
2304
0
      if (!aCreatePluginList && *aPluginsChanged)
2305
0
        break;
2306
0
    }
2307
0
    return NS_OK;
2308
0
}
2309
2310
void
2311
nsPluginHost::IncrementChromeEpoch()
2312
0
{
2313
0
  MOZ_ASSERT(XRE_IsParentProcess());
2314
0
  mPluginEpoch++;
2315
0
}
2316
2317
uint32_t
2318
nsPluginHost::ChromeEpoch()
2319
0
{
2320
0
  MOZ_ASSERT(XRE_IsParentProcess());
2321
0
  return mPluginEpoch;
2322
0
}
2323
2324
uint32_t
2325
nsPluginHost::ChromeEpochForContent()
2326
0
{
2327
0
  MOZ_ASSERT(XRE_IsContentProcess());
2328
0
  return mPluginEpoch;
2329
0
}
2330
2331
void
2332
nsPluginHost::SetChromeEpochForContent(uint32_t aEpoch)
2333
0
{
2334
0
  MOZ_ASSERT(XRE_IsContentProcess());
2335
0
  mPluginEpoch = aEpoch;
2336
0
}
2337
2338
#ifdef XP_WIN
2339
static void
2340
WatchRegKey(uint32_t aRoot, nsCOMPtr<nsIWindowsRegKey>& aKey)
2341
{
2342
  if (aKey) {
2343
    return;
2344
  }
2345
2346
  aKey = do_CreateInstance("@mozilla.org/windows-registry-key;1");
2347
  if (!aKey) {
2348
    return;
2349
  }
2350
  nsresult rv = aKey->Open(aRoot,
2351
                           NS_LITERAL_STRING("Software\\MozillaPlugins"),
2352
                           nsIWindowsRegKey::ACCESS_READ | nsIWindowsRegKey::ACCESS_NOTIFY);
2353
  if (NS_FAILED(rv)) {
2354
    aKey = nullptr;
2355
    return;
2356
  }
2357
  aKey->StartWatching(true);
2358
}
2359
#endif
2360
2361
nsresult nsPluginHost::LoadPlugins()
2362
0
{
2363
0
  // This should only be run in the parent process. On plugin list change, we'll
2364
0
  // update observers in the content process as part of SetPluginsInContent
2365
0
  if (XRE_IsContentProcess()) {
2366
0
    return NS_OK;
2367
0
  }
2368
0
  // do not do anything if it is already done
2369
0
  // use ReloadPlugins() to enforce loading
2370
0
  if (mPluginsLoaded)
2371
0
    return NS_OK;
2372
0
2373
0
  if (mPluginsDisabled)
2374
0
    return NS_OK;
2375
0
2376
#ifdef XP_WIN
2377
  WatchRegKey(nsIWindowsRegKey::ROOT_KEY_LOCAL_MACHINE, mRegKeyHKLM);
2378
  WatchRegKey(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER, mRegKeyHKCU);
2379
#endif
2380
2381
0
  bool pluginschanged;
2382
0
  nsresult rv = FindPlugins(true, &pluginschanged);
2383
0
  if (NS_FAILED(rv))
2384
0
    return rv;
2385
0
2386
0
  // only if plugins have changed will we notify plugin-change observers
2387
0
  if (pluginschanged) {
2388
0
    if (XRE_IsParentProcess()) {
2389
0
      IncrementChromeEpoch();
2390
0
    }
2391
0
2392
0
    nsCOMPtr<nsIObserverService> obsService =
2393
0
      mozilla::services::GetObserverService();
2394
0
    if (obsService)
2395
0
      obsService->NotifyObservers(nullptr, "plugins-list-updated", nullptr);
2396
0
  }
2397
0
2398
0
  return NS_OK;
2399
0
}
2400
2401
nsresult
2402
nsPluginHost::SetPluginsInContent(uint32_t aPluginEpoch,
2403
                                  nsTArray<mozilla::plugins::PluginTag>& aPlugins,
2404
                                  nsTArray<mozilla::plugins::FakePluginTag>& aFakePlugins)
2405
0
{
2406
0
  MOZ_ASSERT(XRE_IsContentProcess());
2407
0
2408
0
  nsTArray<PluginTag> plugins;
2409
0
2410
0
  nsTArray<FakePluginTag> fakePlugins;
2411
0
2412
0
  if (aPluginEpoch != ChromeEpochForContent()) {
2413
0
    // Since we know we're going to be repopulating the lists anyways, trigger a
2414
0
    // reload now to clear out all old entries.
2415
0
    ActuallyReloadPlugins();
2416
0
2417
0
    SetChromeEpochForContent(aPluginEpoch);
2418
0
2419
0
    for (auto tag : aPlugins) {
2420
0
2421
0
      // Don't add the same plugin again.
2422
0
      if (nsPluginTag* existing = PluginWithId(tag.id())) {
2423
0
        UpdateInMemoryPluginInfo(existing);
2424
0
        existing->SetBlocklistState(tag.blocklistState());
2425
0
        continue;
2426
0
      }
2427
0
2428
0
      nsPluginTag *pluginTag = new nsPluginTag(tag.id(),
2429
0
                                               tag.name().get(),
2430
0
                                               tag.description().get(),
2431
0
                                               tag.filename().get(),
2432
0
                                               "", // aFullPath
2433
0
                                               tag.version().get(),
2434
0
                                               nsTArray<nsCString>(tag.mimeTypes()),
2435
0
                                               nsTArray<nsCString>(tag.mimeDescriptions()),
2436
0
                                               nsTArray<nsCString>(tag.extensions()),
2437
0
                                               tag.isFlashPlugin(),
2438
0
                                               tag.supportsAsyncRender(),
2439
0
                                               tag.lastModifiedTime(),
2440
0
                                               tag.isFromExtension(),
2441
0
                                               tag.sandboxLevel(),
2442
0
                                               tag.blocklistState());
2443
0
      AddPluginTag(pluginTag);
2444
0
    }
2445
0
2446
0
    for (const auto& tag : aFakePlugins) {
2447
0
      // Don't add the same plugin again.
2448
0
      for (const auto& existingTag : mFakePlugins) {
2449
0
        if (existingTag->Id() == tag.id()) {
2450
0
          continue;
2451
0
        }
2452
0
      }
2453
0
2454
0
      RefPtr<nsFakePluginTag> pluginTag =
2455
0
      *mFakePlugins.AppendElement(new nsFakePluginTag(tag.id(),
2456
0
                                                      mozilla::ipc::DeserializeURI(tag.handlerURI()),
2457
0
                                                      tag.name().get(),
2458
0
                                                      tag.description().get(),
2459
0
                                                      tag.mimeTypes(),
2460
0
                                                      tag.mimeDescriptions(),
2461
0
                                                      tag.extensions(),
2462
0
                                                      tag.niceName(),
2463
0
                                                      tag.sandboxScript()));
2464
0
      nsAutoCString disableFullPage;
2465
0
      Preferences::GetCString(kPrefDisableFullPage, disableFullPage);
2466
0
      for (uint32_t i = 0; i < pluginTag->MimeTypes().Length(); i++) {
2467
0
        if (!IsTypeInList(pluginTag->MimeTypes()[i], disableFullPage)) {
2468
0
          RegisterWithCategoryManager(pluginTag->MimeTypes()[i],
2469
0
                                      ePluginRegister);
2470
0
        }
2471
0
      }
2472
0
    }
2473
0
2474
0
    nsCOMPtr<nsIObserverService> obsService =
2475
0
      mozilla::services::GetObserverService();
2476
0
    if (obsService) {
2477
0
      obsService->NotifyObservers(nullptr, "plugins-list-updated", nullptr);
2478
0
    }
2479
0
  }
2480
0
2481
0
  mPluginsLoaded = true;
2482
0
  return NS_OK;
2483
0
}
2484
2485
// if aCreatePluginList is false we will just scan for plugins
2486
// and see if any changes have been made to the plugins.
2487
// This is needed in ReloadPlugins to prevent possible recursive reloads
2488
nsresult nsPluginHost::FindPlugins(bool aCreatePluginList, bool * aPluginsChanged)
2489
0
{
2490
0
  Telemetry::AutoTimer<Telemetry::FIND_PLUGINS> telemetry;
2491
0
2492
0
  NS_ENSURE_ARG_POINTER(aPluginsChanged);
2493
0
2494
0
  *aPluginsChanged = false;
2495
0
2496
0
  // If plugins are found or change, the content process will be notified by the
2497
0
  // parent process. Bail out early if this is called from the content process.
2498
0
  if (XRE_IsContentProcess()) {
2499
0
    return NS_OK;
2500
0
  }
2501
0
2502
0
  nsresult rv;
2503
0
2504
0
  // Read cached plugins info. If the profile isn't yet available then don't
2505
0
  // scan for plugins
2506
0
  if (ReadPluginInfo() == NS_ERROR_NOT_AVAILABLE)
2507
0
    return NS_OK;
2508
0
2509
#ifdef XP_WIN
2510
  // Failure here is not a show-stopper so just warn.
2511
  rv = EnsurePrivateDirServiceProvider();
2512
  NS_ASSERTION(NS_SUCCEEDED(rv), "Failed to register dir service provider.");
2513
#endif /* XP_WIN */
2514
2515
0
  nsCOMPtr<nsIProperties> dirService(do_GetService(kDirectoryServiceContractID, &rv));
2516
0
  if (NS_FAILED(rv))
2517
0
    return rv;
2518
0
2519
0
  nsCOMPtr<nsISimpleEnumerator> dirList;
2520
0
2521
0
  // Scan plugins directories;
2522
0
  // don't pass aPluginsChanged directly, to prevent its
2523
0
  // possible reset in subsequent ScanPluginsDirectory calls
2524
0
  bool pluginschanged = false;
2525
0
2526
0
  // Scan the app-defined list of plugin dirs.
2527
0
  rv = dirService->Get(NS_APP_PLUGINS_DIR_LIST, NS_GET_IID(nsISimpleEnumerator), getter_AddRefs(dirList));
2528
0
  if (NS_SUCCEEDED(rv)) {
2529
0
    ScanPluginsDirectoryList(dirList, aCreatePluginList, &pluginschanged);
2530
0
2531
0
    if (pluginschanged)
2532
0
      *aPluginsChanged = true;
2533
0
2534
0
    // if we are just looking for possible changes,
2535
0
    // no need to proceed if changes are detected
2536
0
    if (!aCreatePluginList && *aPluginsChanged) {
2537
0
      NS_ITERATIVE_UNREF_LIST(RefPtr<nsPluginTag>, mCachedPlugins, mNext);
2538
0
      NS_ITERATIVE_UNREF_LIST(RefPtr<nsInvalidPluginTag>, mInvalidPlugins, mNext);
2539
0
      return NS_OK;
2540
0
    }
2541
0
  }
2542
0
2543
0
  mPluginsLoaded = true; // at this point 'some' plugins have been loaded,
2544
0
                            // the rest is optional
2545
0
2546
#ifdef XP_WIN
2547
  bool bScanPLIDs = Preferences::GetBool("plugin.scan.plid.all", false);
2548
2549
    // Now lets scan any PLID directories
2550
  if (bScanPLIDs && mPrivateDirServiceProvider) {
2551
    rv = mPrivateDirServiceProvider->GetPLIDDirectories(getter_AddRefs(dirList));
2552
    if (NS_SUCCEEDED(rv)) {
2553
      ScanPluginsDirectoryList(dirList, aCreatePluginList, &pluginschanged);
2554
2555
      if (pluginschanged)
2556
        *aPluginsChanged = true;
2557
2558
      // if we are just looking for possible changes,
2559
      // no need to proceed if changes are detected
2560
      if (!aCreatePluginList && *aPluginsChanged) {
2561
        NS_ITERATIVE_UNREF_LIST(RefPtr<nsPluginTag>, mCachedPlugins, mNext);
2562
        NS_ITERATIVE_UNREF_LIST(RefPtr<nsInvalidPluginTag>, mInvalidPlugins, mNext);
2563
        return NS_OK;
2564
      }
2565
    }
2566
  }
2567
#endif
2568
2569
0
  // We should also consider plugins to have changed if any plugins have been removed.
2570
0
  // We'll know if any were removed if they weren't taken out of the cached plugins list
2571
0
  // during our scan, thus we can assume something was removed if the cached plugins list
2572
0
  // contains anything.
2573
0
  if (!*aPluginsChanged && mCachedPlugins) {
2574
0
    *aPluginsChanged = true;
2575
0
  }
2576
0
2577
0
  // Remove unseen invalid plugins
2578
0
  RefPtr<nsInvalidPluginTag> invalidPlugins = mInvalidPlugins;
2579
0
  while (invalidPlugins) {
2580
0
    if (!invalidPlugins->mSeen) {
2581
0
      RefPtr<nsInvalidPluginTag> invalidPlugin = invalidPlugins;
2582
0
2583
0
      if (invalidPlugin->mPrev) {
2584
0
        invalidPlugin->mPrev->mNext = invalidPlugin->mNext;
2585
0
      }
2586
0
      else {
2587
0
        mInvalidPlugins = invalidPlugin->mNext;
2588
0
      }
2589
0
      if (invalidPlugin->mNext) {
2590
0
        invalidPlugin->mNext->mPrev = invalidPlugin->mPrev;
2591
0
      }
2592
0
2593
0
      invalidPlugins = invalidPlugin->mNext;
2594
0
2595
0
      invalidPlugin->mPrev = nullptr;
2596
0
      invalidPlugin->mNext = nullptr;
2597
0
    }
2598
0
    else {
2599
0
      invalidPlugins->mSeen = false;
2600
0
      invalidPlugins = invalidPlugins->mNext;
2601
0
    }
2602
0
  }
2603
0
2604
0
  // if we are not creating the list, there is no need to proceed
2605
0
  if (!aCreatePluginList) {
2606
0
    NS_ITERATIVE_UNREF_LIST(RefPtr<nsPluginTag>, mCachedPlugins, mNext);
2607
0
    NS_ITERATIVE_UNREF_LIST(RefPtr<nsInvalidPluginTag>, mInvalidPlugins, mNext);
2608
0
    return NS_OK;
2609
0
  }
2610
0
2611
0
  // if we are creating the list, it is already done;
2612
0
  // update the plugins info cache if changes are detected
2613
0
  if (*aPluginsChanged)
2614
0
    WritePluginInfo();
2615
0
2616
0
  // No more need for cached plugins. Clear it up.
2617
0
  NS_ITERATIVE_UNREF_LIST(RefPtr<nsPluginTag>, mCachedPlugins, mNext);
2618
0
  NS_ITERATIVE_UNREF_LIST(RefPtr<nsInvalidPluginTag>, mInvalidPlugins, mNext);
2619
0
2620
0
  return NS_OK;
2621
0
}
2622
2623
nsresult
2624
nsPluginHost::SendPluginsToContent()
2625
0
{
2626
0
  MOZ_ASSERT(XRE_IsParentProcess());
2627
0
2628
0
  nsTArray<PluginTag> pluginTags;
2629
0
  nsTArray<FakePluginTag> fakePluginTags;
2630
0
  // Load plugins so that the epoch is correct.
2631
0
  nsresult rv = LoadPlugins();
2632
0
  if (NS_FAILED(rv)) {
2633
0
    return rv;
2634
0
  }
2635
0
2636
0
  uint32_t newPluginEpoch = ChromeEpoch();
2637
0
2638
0
  nsTArray<nsCOMPtr<nsIInternalPluginTag>> plugins;
2639
0
  GetPlugins(plugins, true);
2640
0
2641
0
  for (size_t i = 0; i < plugins.Length(); i++) {
2642
0
    nsCOMPtr<nsIInternalPluginTag> basetag = plugins[i];
2643
0
2644
0
    nsCOMPtr<nsIFakePluginTag> faketag = do_QueryInterface(basetag);
2645
0
    if (faketag) {
2646
0
      /// FIXME-jsplugins - We need to add a nsIInternalPluginTag->AsNative() to
2647
0
      /// avoid this hacky static cast
2648
0
      nsFakePluginTag* tag = static_cast<nsFakePluginTag*>(basetag.get());
2649
0
      mozilla::ipc::URIParams handlerURI;
2650
0
      SerializeURI(tag->HandlerURI(), handlerURI);
2651
0
      fakePluginTags.AppendElement(FakePluginTag(tag->Id(),
2652
0
                                                 handlerURI,
2653
0
                                                 tag->Name(),
2654
0
                                                 tag->Description(),
2655
0
                                                 tag->MimeTypes(),
2656
0
                                                 tag->MimeDescriptions(),
2657
0
                                                 tag->Extensions(),
2658
0
                                                 tag->GetNiceFileName(),
2659
0
                                                 tag->SandboxScript()));
2660
0
      continue;
2661
0
    }
2662
0
2663
0
    /// FIXME-jsplugins - We need to cleanup the various plugintag classes
2664
0
    /// to be more sane and avoid this dance
2665
0
    nsPluginTag *tag = static_cast<nsPluginTag *>(basetag.get());
2666
0
2667
0
    uint32_t blocklistState;
2668
0
    if (NS_WARN_IF(NS_FAILED(tag->GetBlocklistState(&blocklistState)))) {
2669
0
      return NS_ERROR_FAILURE;
2670
0
    }
2671
0
2672
0
    pluginTags.AppendElement(PluginTag(tag->mId,
2673
0
                                       tag->Name(),
2674
0
                                       tag->Description(),
2675
0
                                       tag->MimeTypes(),
2676
0
                                       tag->MimeDescriptions(),
2677
0
                                       tag->Extensions(),
2678
0
                                       tag->mIsFlashPlugin,
2679
0
                                       tag->mSupportsAsyncRender,
2680
0
                                       tag->FileName(),
2681
0
                                       tag->Version(),
2682
0
                                       tag->mLastModifiedTime,
2683
0
                                       tag->IsFromExtension(),
2684
0
                                       tag->mSandboxLevel,
2685
0
                                       blocklistState));
2686
0
  }
2687
0
  nsTArray<dom::ContentParent*> parents;
2688
0
  dom::ContentParent::GetAll(parents);
2689
0
  for (auto p : parents)
2690
0
  {
2691
0
    Unused << p->SendSetPluginList(newPluginEpoch, pluginTags, fakePluginTags);
2692
0
  }
2693
0
  return NS_OK;
2694
0
}
2695
2696
void
2697
nsPluginHost::UpdateInMemoryPluginInfo(nsPluginTag* aPluginTag)
2698
0
{
2699
0
  NS_ITERATIVE_UNREF_LIST(RefPtr<nsPluginTag>, mCachedPlugins, mNext);
2700
0
  NS_ITERATIVE_UNREF_LIST(RefPtr<nsInvalidPluginTag>, mInvalidPlugins, mNext);
2701
0
2702
0
  if (!aPluginTag) {
2703
0
    return;
2704
0
  }
2705
0
2706
0
  // Update types with category manager
2707
0
  nsAutoCString disableFullPage;
2708
0
  Preferences::GetCString(kPrefDisableFullPage, disableFullPage);
2709
0
  for (uint32_t i = 0; i < aPluginTag->MimeTypes().Length(); i++) {
2710
0
    nsRegisterType shouldRegister;
2711
0
2712
0
    if (IsTypeInList(aPluginTag->MimeTypes()[i], disableFullPage)) {
2713
0
      shouldRegister = ePluginUnregister;
2714
0
    } else {
2715
0
      nsPluginTag *plugin = FindNativePluginForType(aPluginTag->MimeTypes()[i],
2716
0
                                                    true);
2717
0
      shouldRegister = plugin ? ePluginRegister : ePluginUnregister;
2718
0
    }
2719
0
2720
0
    RegisterWithCategoryManager(aPluginTag->MimeTypes()[i], shouldRegister);
2721
0
  }
2722
0
2723
0
  nsCOMPtr<nsIObserverService> obsService =
2724
0
    mozilla::services::GetObserverService();
2725
0
  if (obsService)
2726
0
    obsService->NotifyObservers(nullptr, "plugin-info-updated", nullptr);
2727
0
}
2728
2729
// This function is not relevant for fake plugins.
2730
void
2731
nsPluginHost::UpdatePluginInfo(nsPluginTag* aPluginTag)
2732
0
{
2733
0
  MOZ_ASSERT(XRE_IsParentProcess());
2734
0
2735
0
  ReadPluginInfo();
2736
0
  WritePluginInfo();
2737
0
2738
0
  IncrementChromeEpoch();
2739
0
2740
0
  UpdateInMemoryPluginInfo(aPluginTag);
2741
0
}
2742
2743
/* static */ bool
2744
nsPluginHost::IsTypeWhitelisted(const char *aMimeType)
2745
0
{
2746
0
  nsAutoCString whitelist;
2747
0
  Preferences::GetCString(kPrefWhitelist, whitelist);
2748
0
  if (whitelist.IsEmpty()) {
2749
0
    return true;
2750
0
  }
2751
0
  nsDependentCString wrap(aMimeType);
2752
0
  return IsTypeInList(wrap, whitelist);
2753
0
}
2754
2755
/* static */ bool
2756
nsPluginHost::ShouldLoadTypeInParent(const nsACString& aMimeType)
2757
0
{
2758
0
  nsCString prefName(kPrefLoadInParentPrefix);
2759
0
  prefName += aMimeType;
2760
0
  return Preferences::GetBool(prefName.get(), false);
2761
0
}
2762
2763
void
2764
nsPluginHost::RegisterWithCategoryManager(const nsCString& aMimeType,
2765
                                          nsRegisterType aType)
2766
0
{
2767
0
  PLUGIN_LOG(PLUGIN_LOG_NORMAL,
2768
0
             ("nsPluginTag::RegisterWithCategoryManager type = %s, removing = %s\n",
2769
0
              aMimeType.get(), aType == ePluginUnregister ? "yes" : "no"));
2770
0
2771
0
  nsCOMPtr<nsICategoryManager> catMan =
2772
0
    do_GetService(NS_CATEGORYMANAGER_CONTRACTID);
2773
0
  if (!catMan) {
2774
0
    return;
2775
0
  }
2776
0
2777
0
  NS_NAMED_LITERAL_CSTRING(contractId,
2778
0
                           "@mozilla.org/content/plugin/document-loader-factory;1");
2779
0
2780
0
  if (aType == ePluginRegister) {
2781
0
    catMan->AddCategoryEntry("Gecko-Content-Viewers",
2782
0
                             aMimeType,
2783
0
                             contractId,
2784
0
                             false, /* persist: broken by bug 193031 */
2785
0
                             mOverrideInternalTypes);
2786
0
  } else {
2787
0
    if (aType == ePluginMaybeUnregister) {
2788
0
      // Bail out if this type is still used by an enabled plugin
2789
0
      if (HavePluginForType(aMimeType)) {
2790
0
        return;
2791
0
      }
2792
0
    } else {
2793
0
      MOZ_ASSERT(aType == ePluginUnregister, "Unknown nsRegisterType");
2794
0
    }
2795
0
2796
0
    // Only delete the entry if a plugin registered for it
2797
0
    nsCString value;
2798
0
    nsresult rv = catMan->GetCategoryEntry("Gecko-Content-Viewers",
2799
0
                                           aMimeType, value);
2800
0
    if (NS_SUCCEEDED(rv) && value == contractId) {
2801
0
      catMan->DeleteCategoryEntry("Gecko-Content-Viewers",
2802
0
                                  aMimeType, true);
2803
0
    }
2804
0
  }
2805
0
}
2806
2807
nsresult
2808
nsPluginHost::WritePluginInfo()
2809
0
{
2810
0
  MOZ_ASSERT(XRE_IsParentProcess());
2811
0
2812
0
  nsresult rv = NS_OK;
2813
0
  nsCOMPtr<nsIProperties> directoryService(do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID,&rv));
2814
0
  if (NS_FAILED(rv))
2815
0
    return rv;
2816
0
2817
0
  directoryService->Get(NS_APP_USER_PROFILE_50_DIR, NS_GET_IID(nsIFile),
2818
0
                        getter_AddRefs(mPluginRegFile));
2819
0
2820
0
  if (!mPluginRegFile)
2821
0
    return NS_ERROR_FAILURE;
2822
0
2823
0
  PRFileDesc* fd = nullptr;
2824
0
2825
0
  nsCOMPtr<nsIFile> pluginReg;
2826
0
2827
0
  rv = mPluginRegFile->Clone(getter_AddRefs(pluginReg));
2828
0
  if (NS_FAILED(rv))
2829
0
    return rv;
2830
0
2831
0
  nsAutoCString filename(kPluginRegistryFilename);
2832
0
  filename.AppendLiteral(".tmp");
2833
0
  rv = pluginReg->AppendNative(filename);
2834
0
  if (NS_FAILED(rv))
2835
0
    return rv;
2836
0
2837
0
  rv = pluginReg->OpenNSPRFileDesc(PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE, 0600, &fd);
2838
0
  if (NS_FAILED(rv))
2839
0
    return rv;
2840
0
2841
0
  nsCOMPtr<nsIXULRuntime> runtime = do_GetService("@mozilla.org/xre/runtime;1");
2842
0
  if (!runtime) {
2843
0
    return NS_ERROR_FAILURE;
2844
0
  }
2845
0
2846
0
  nsAutoCString arch;
2847
0
  rv = runtime->GetXPCOMABI(arch);
2848
0
  if (NS_FAILED(rv)) {
2849
0
    return rv;
2850
0
  }
2851
0
2852
0
  bool flashOnly = Preferences::GetBool("plugin.load_flash_only", true);
2853
0
2854
0
  PR_fprintf(fd, "Generated File. Do not edit.\n");
2855
0
2856
0
  PR_fprintf(fd, "\n[HEADER]\nVersion%c%s%c%c%c\nArch%c%s%c%c\n",
2857
0
             PLUGIN_REGISTRY_FIELD_DELIMITER,
2858
0
             kPluginRegistryVersion,
2859
0
             flashOnly ? 't' : 'f',
2860
0
             PLUGIN_REGISTRY_FIELD_DELIMITER,
2861
0
             PLUGIN_REGISTRY_END_OF_LINE_MARKER,
2862
0
             PLUGIN_REGISTRY_FIELD_DELIMITER,
2863
0
             arch.get(),
2864
0
             PLUGIN_REGISTRY_FIELD_DELIMITER,
2865
0
             PLUGIN_REGISTRY_END_OF_LINE_MARKER);
2866
0
2867
0
  // Store all plugins in the mPlugins list - all plugins currently in use.
2868
0
  PR_fprintf(fd, "\n[PLUGINS]\n");
2869
0
2870
0
  for (nsPluginTag *tag = mPlugins; tag; tag = tag->mNext) {
2871
0
    // store each plugin info into the registry
2872
0
    // filename & fullpath are on separate line
2873
0
    // because they can contain field delimiter char
2874
0
    PR_fprintf(fd, "%s%c%c\n%s%c%c\n%s%c%c\n",
2875
0
      (tag->FileName().get()),
2876
0
      PLUGIN_REGISTRY_FIELD_DELIMITER,
2877
0
      PLUGIN_REGISTRY_END_OF_LINE_MARKER,
2878
0
      (tag->mFullPath.get()),
2879
0
      PLUGIN_REGISTRY_FIELD_DELIMITER,
2880
0
      PLUGIN_REGISTRY_END_OF_LINE_MARKER,
2881
0
      (tag->Version().get()),
2882
0
      PLUGIN_REGISTRY_FIELD_DELIMITER,
2883
0
      PLUGIN_REGISTRY_END_OF_LINE_MARKER);
2884
0
2885
0
    // lastModifiedTimeStamp|canUnload|tag->mFlags|fromExtension|blocklistState
2886
0
    PR_fprintf(fd, "%lld%c%d%c%lu%c%d%c%d%c%c\n",
2887
0
      tag->mLastModifiedTime,
2888
0
      PLUGIN_REGISTRY_FIELD_DELIMITER,
2889
0
      false, // did store whether or not to unload in-process plugins
2890
0
      PLUGIN_REGISTRY_FIELD_DELIMITER,
2891
0
      0, // legacy field for flags
2892
0
      PLUGIN_REGISTRY_FIELD_DELIMITER,
2893
0
      tag->IsFromExtension(),
2894
0
      PLUGIN_REGISTRY_FIELD_DELIMITER,
2895
0
      tag->BlocklistState(),
2896
0
      PLUGIN_REGISTRY_FIELD_DELIMITER,
2897
0
      PLUGIN_REGISTRY_END_OF_LINE_MARKER);
2898
0
2899
0
    //description, name & mtypecount are on separate line
2900
0
    PR_fprintf(fd, "%s%c%c\n%s%c%c\n%d\n",
2901
0
      (tag->Description().get()),
2902
0
      PLUGIN_REGISTRY_FIELD_DELIMITER,
2903
0
      PLUGIN_REGISTRY_END_OF_LINE_MARKER,
2904
0
      (tag->Name().get()),
2905
0
      PLUGIN_REGISTRY_FIELD_DELIMITER,
2906
0
      PLUGIN_REGISTRY_END_OF_LINE_MARKER,
2907
0
      tag->MimeTypes().Length());
2908
0
2909
0
    // Add in each mimetype this plugin supports
2910
0
    for (uint32_t i = 0; i < tag->MimeTypes().Length(); i++) {
2911
0
      PR_fprintf(fd, "%d%c%s%c%s%c%s%c%c\n",
2912
0
        i,PLUGIN_REGISTRY_FIELD_DELIMITER,
2913
0
        (tag->MimeTypes()[i].get()),
2914
0
        PLUGIN_REGISTRY_FIELD_DELIMITER,
2915
0
        (tag->MimeDescriptions()[i].get()),
2916
0
        PLUGIN_REGISTRY_FIELD_DELIMITER,
2917
0
        (tag->Extensions()[i].get()),
2918
0
        PLUGIN_REGISTRY_FIELD_DELIMITER,
2919
0
        PLUGIN_REGISTRY_END_OF_LINE_MARKER);
2920
0
    }
2921
0
  }
2922
0
2923
0
  PR_fprintf(fd, "\n[INVALID]\n");
2924
0
2925
0
  RefPtr<nsInvalidPluginTag> invalidPlugins = mInvalidPlugins;
2926
0
  while (invalidPlugins) {
2927
0
    // fullPath
2928
0
    PR_fprintf(fd, "%s%c%c\n",
2929
0
      (!invalidPlugins->mFullPath.IsEmpty() ? invalidPlugins->mFullPath.get() : ""),
2930
0
      PLUGIN_REGISTRY_FIELD_DELIMITER,
2931
0
      PLUGIN_REGISTRY_END_OF_LINE_MARKER);
2932
0
2933
0
    // lastModifiedTimeStamp
2934
0
    PR_fprintf(fd, "%lld%c%c\n",
2935
0
      invalidPlugins->mLastModifiedTime,
2936
0
      PLUGIN_REGISTRY_FIELD_DELIMITER,
2937
0
      PLUGIN_REGISTRY_END_OF_LINE_MARKER);
2938
0
2939
0
    invalidPlugins = invalidPlugins->mNext;
2940
0
  }
2941
0
2942
0
  PRStatus prrc;
2943
0
  prrc = PR_Close(fd);
2944
0
  if (prrc != PR_SUCCESS) {
2945
0
    // we should obtain a refined value based on prrc;
2946
0
    rv = NS_ERROR_FAILURE;
2947
0
    MOZ_ASSERT(false, "PR_Close() failed.");
2948
0
    return rv;
2949
0
  }
2950
0
  nsCOMPtr<nsIFile> parent;
2951
0
  rv = pluginReg->GetParent(getter_AddRefs(parent));
2952
0
  NS_ENSURE_SUCCESS(rv, rv);
2953
0
  rv = pluginReg->MoveToNative(parent, kPluginRegistryFilename);
2954
0
  return rv;
2955
0
}
2956
2957
nsresult
2958
nsPluginHost::ReadPluginInfo()
2959
0
{
2960
0
  MOZ_ASSERT(XRE_IsParentProcess());
2961
0
2962
0
  const long PLUGIN_REG_MIMETYPES_ARRAY_SIZE = 12;
2963
0
  const long PLUGIN_REG_MAX_MIMETYPES = 1000;
2964
0
2965
0
  nsresult rv;
2966
0
2967
0
  nsCOMPtr<nsIProperties> directoryService(do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID,&rv));
2968
0
  if (NS_FAILED(rv))
2969
0
    return rv;
2970
0
2971
0
  directoryService->Get(NS_APP_USER_PROFILE_50_DIR, NS_GET_IID(nsIFile),
2972
0
                        getter_AddRefs(mPluginRegFile));
2973
0
2974
0
  if (!mPluginRegFile) {
2975
0
    // There is no profile yet, this will tell us if there is going to be one
2976
0
    // in the future.
2977
0
    directoryService->Get(NS_APP_PROFILE_DIR_STARTUP, NS_GET_IID(nsIFile),
2978
0
                          getter_AddRefs(mPluginRegFile));
2979
0
    if (!mPluginRegFile)
2980
0
      return NS_ERROR_FAILURE;
2981
0
2982
0
    return NS_ERROR_NOT_AVAILABLE;
2983
0
  }
2984
0
2985
0
  PRFileDesc* fd = nullptr;
2986
0
2987
0
  nsCOMPtr<nsIFile> pluginReg;
2988
0
2989
0
  rv = mPluginRegFile->Clone(getter_AddRefs(pluginReg));
2990
0
  if (NS_FAILED(rv))
2991
0
    return rv;
2992
0
2993
0
  rv = pluginReg->AppendNative(kPluginRegistryFilename);
2994
0
  if (NS_FAILED(rv))
2995
0
    return rv;
2996
0
2997
0
  int64_t fileSize;
2998
0
  rv = pluginReg->GetFileSize(&fileSize);
2999
0
  if (NS_FAILED(rv))
3000
0
    return rv;
3001
0
3002
0
  if (fileSize > INT32_MAX) {
3003
0
    return NS_ERROR_FAILURE;
3004
0
  }
3005
0
  int32_t flen = int32_t(fileSize);
3006
0
  if (flen == 0) {
3007
0
    NS_WARNING("Plugins Registry Empty!");
3008
0
    return NS_OK; // ERROR CONDITION
3009
0
  }
3010
0
3011
0
  nsPluginManifestLineReader reader;
3012
0
  char* registry = reader.Init(flen);
3013
0
  if (!registry)
3014
0
    return NS_ERROR_OUT_OF_MEMORY;
3015
0
3016
0
  rv = pluginReg->OpenNSPRFileDesc(PR_RDONLY, 0444, &fd);
3017
0
  if (NS_FAILED(rv))
3018
0
    return rv;
3019
0
3020
0
  // set rv to return an error on goto out
3021
0
  rv = NS_ERROR_FAILURE;
3022
0
3023
0
  // We know how many octes we are supposed to read.
3024
0
  // So let use the busy_beaver_PR_Read version.
3025
0
  int32_t bread = busy_beaver_PR_Read(fd, registry, flen);
3026
0
3027
0
  PRStatus prrc;
3028
0
  prrc = PR_Close(fd);
3029
0
  if (prrc != PR_SUCCESS) {
3030
0
    // Strange error: this is one of those "Should not happen" error.
3031
0
    // we may want to report something more refined than  NS_ERROR_FAILURE.
3032
0
    MOZ_ASSERT(false, "PR_Close() failed.");
3033
0
    return rv;
3034
0
  }
3035
0
3036
0
  // short read error, so to speak.
3037
0
  if (flen > bread)
3038
0
    return rv;
3039
0
3040
0
  if (!ReadSectionHeader(reader, "HEADER"))
3041
0
    return rv;;
3042
0
3043
0
  if (!reader.NextLine())
3044
0
    return rv;
3045
0
3046
0
  char* values[6];
3047
0
3048
0
  // VersionLiteral, kPluginRegistryVersion
3049
0
  if (2 != reader.ParseLine(values, 2))
3050
0
    return rv;
3051
0
3052
0
  // VersionLiteral
3053
0
  if (PL_strcmp(values[0], "Version"))
3054
0
    return rv;
3055
0
3056
0
  // If we're reading an old registry, ignore it
3057
0
  // If we flipped the flash-only pref, ignore it
3058
0
  bool flashOnly = Preferences::GetBool("plugin.load_flash_only", true);
3059
0
  nsAutoCString expectedVersion(kPluginRegistryVersion);
3060
0
  expectedVersion.Append(flashOnly ? 't' : 'f');
3061
0
3062
0
  if (!expectedVersion.Equals(values[1])) {
3063
0
    return rv;
3064
0
  }
3065
0
3066
0
  char* archValues[6];
3067
0
  if (!reader.NextLine()) {
3068
0
    return rv;
3069
0
  }
3070
0
3071
0
  // ArchLiteral, Architecture
3072
0
  if (2 != reader.ParseLine(archValues, 2)) {
3073
0
    return rv;
3074
0
  }
3075
0
3076
0
  // ArchLiteral
3077
0
  if (PL_strcmp(archValues[0], "Arch")) {
3078
0
    return rv;
3079
0
  }
3080
0
3081
0
  nsCOMPtr<nsIXULRuntime> runtime = do_GetService("@mozilla.org/xre/runtime;1");
3082
0
  if (!runtime) {
3083
0
    return rv;
3084
0
  }
3085
0
3086
0
  nsAutoCString arch;
3087
0
  if (NS_FAILED(runtime->GetXPCOMABI(arch))) {
3088
0
    return rv;
3089
0
  }
3090
0
3091
0
  // If this is a registry from a different architecture then don't attempt to read it
3092
0
  if (PL_strcmp(archValues[1], arch.get())) {
3093
0
    return rv;
3094
0
  }
3095
0
3096
0
  if (!ReadSectionHeader(reader, "PLUGINS"))
3097
0
    return rv;
3098
0
3099
0
  while (reader.NextLine()) {
3100
0
    if (*reader.LinePtr() == '[') {
3101
0
      break;
3102
0
    }
3103
0
3104
0
    const char* filename = reader.LinePtr();
3105
0
    if (!reader.NextLine())
3106
0
      return rv;
3107
0
3108
0
    const char* fullpath = reader.LinePtr();
3109
0
    if (!reader.NextLine())
3110
0
      return rv;
3111
0
3112
0
    const char *version;
3113
0
    version = reader.LinePtr();
3114
0
    if (!reader.NextLine())
3115
0
      return rv;
3116
0
3117
0
    // lastModifiedTimeStamp|canUnload|tag.mFlag|fromExtension|blocklistState
3118
0
    if (5 != reader.ParseLine(values, 5))
3119
0
      return rv;
3120
0
3121
0
    int64_t lastmod = nsCRT::atoll(values[0]);
3122
0
    bool fromExtension = atoi(values[3]);
3123
0
    uint16_t blocklistState = atoi(values[4]);
3124
0
    if (!reader.NextLine())
3125
0
      return rv;
3126
0
3127
0
    char *description = reader.LinePtr();
3128
0
    if (!reader.NextLine())
3129
0
      return rv;
3130
0
3131
0
    const char *name = reader.LinePtr();
3132
0
    if (!reader.NextLine())
3133
0
      return rv;
3134
0
3135
0
    long mimetypecount = std::strtol(reader.LinePtr(), nullptr, 10);
3136
0
    if (mimetypecount == LONG_MAX || mimetypecount == LONG_MIN ||
3137
0
        mimetypecount >= PLUGIN_REG_MAX_MIMETYPES || mimetypecount < 0) {
3138
0
      return NS_ERROR_FAILURE;
3139
0
    }
3140
0
3141
0
    char *stackalloced[PLUGIN_REG_MIMETYPES_ARRAY_SIZE * 3];
3142
0
    char **mimetypes;
3143
0
    char **mimedescriptions;
3144
0
    char **extensions;
3145
0
    char **heapalloced = 0;
3146
0
    if (mimetypecount > PLUGIN_REG_MIMETYPES_ARRAY_SIZE - 1) {
3147
0
      heapalloced = new char *[mimetypecount * 3];
3148
0
      mimetypes = heapalloced;
3149
0
    } else {
3150
0
      mimetypes = stackalloced;
3151
0
    }
3152
0
    mimedescriptions = mimetypes + mimetypecount;
3153
0
    extensions = mimedescriptions + mimetypecount;
3154
0
3155
0
    int mtr = 0; //mimetype read
3156
0
    for (; mtr < mimetypecount; mtr++) {
3157
0
      if (!reader.NextLine())
3158
0
        break;
3159
0
3160
0
      //line number|mimetype|description|extension
3161
0
      if (4 != reader.ParseLine(values, 4))
3162
0
        break;
3163
0
      int line = atoi(values[0]);
3164
0
      if (line != mtr)
3165
0
        break;
3166
0
      mimetypes[mtr] = values[1];
3167
0
      mimedescriptions[mtr] = values[2];
3168
0
      extensions[mtr] = values[3];
3169
0
    }
3170
0
3171
0
    if (mtr != mimetypecount) {
3172
0
      delete [] heapalloced;
3173
0
      return rv;
3174
0
    }
3175
0
3176
0
    RefPtr<nsPluginTag> tag = new nsPluginTag(name,
3177
0
      description,
3178
0
      filename,
3179
0
      fullpath,
3180
0
      version,
3181
0
      (const char* const*)mimetypes,
3182
0
      (const char* const*)mimedescriptions,
3183
0
      (const char* const*)extensions,
3184
0
      mimetypecount, lastmod, fromExtension, blocklistState, true);
3185
0
3186
0
    delete [] heapalloced;
3187
0
3188
0
    // Import flags from registry into prefs for old registry versions
3189
0
    MOZ_LOG(nsPluginLogging::gPluginLog, PLUGIN_LOG_BASIC,
3190
0
      ("LoadCachedPluginsInfo : Loading Cached plugininfo for %s\n", tag->FileName().get()));
3191
0
3192
0
    tag->mNext = mCachedPlugins;
3193
0
    mCachedPlugins = tag;
3194
0
  }
3195
0
3196
0
  if (!ReadSectionHeader(reader, "INVALID")) {
3197
0
    return rv;
3198
0
  }
3199
0
3200
0
  while (reader.NextLine()) {
3201
0
    const char *fullpath = reader.LinePtr();
3202
0
    if (!reader.NextLine()) {
3203
0
      return rv;
3204
0
    }
3205
0
3206
0
    const char *lastModifiedTimeStamp = reader.LinePtr();
3207
0
    int64_t lastmod = nsCRT::atoll(lastModifiedTimeStamp);
3208
0
3209
0
    RefPtr<nsInvalidPluginTag> invalidTag = new nsInvalidPluginTag(fullpath, lastmod);
3210
0
3211
0
    invalidTag->mNext = mInvalidPlugins;
3212
0
    if (mInvalidPlugins) {
3213
0
      mInvalidPlugins->mPrev = invalidTag;
3214
0
    }
3215
0
    mInvalidPlugins = invalidTag;
3216
0
  }
3217
0
3218
0
  return NS_OK;
3219
0
}
3220
3221
void
3222
nsPluginHost::RemoveCachedPluginsInfo(const char *filePath, nsPluginTag **result)
3223
0
{
3224
0
  RefPtr<nsPluginTag> prev;
3225
0
  RefPtr<nsPluginTag> tag = mCachedPlugins;
3226
0
  while (tag)
3227
0
  {
3228
0
    if (tag->mFullPath.Equals(filePath)) {
3229
0
      // Found it. Remove it from our list
3230
0
      if (prev)
3231
0
        prev->mNext = tag->mNext;
3232
0
      else
3233
0
        mCachedPlugins = tag->mNext;
3234
0
      tag->mNext = nullptr;
3235
0
      *result = tag;
3236
0
      NS_ADDREF(*result);
3237
0
      break;
3238
0
    }
3239
0
    prev = tag;
3240
0
    tag = tag->mNext;
3241
0
  }
3242
0
}
3243
3244
#ifdef XP_WIN
3245
nsresult
3246
nsPluginHost::EnsurePrivateDirServiceProvider()
3247
{
3248
  if (!mPrivateDirServiceProvider) {
3249
    nsresult rv;
3250
    mPrivateDirServiceProvider = new nsPluginDirServiceProvider();
3251
    nsCOMPtr<nsIDirectoryService> dirService(do_GetService(kDirectoryServiceContractID, &rv));
3252
    if (NS_FAILED(rv))
3253
      return rv;
3254
    rv = dirService->RegisterProvider(mPrivateDirServiceProvider);
3255
    if (NS_FAILED(rv))
3256
      return rv;
3257
  }
3258
  return NS_OK;
3259
}
3260
#endif /* XP_WIN */
3261
3262
nsresult nsPluginHost::NewPluginURLStream(const nsString& aURL,
3263
                                          nsNPAPIPluginInstance *aInstance,
3264
                                          nsNPAPIPluginStreamListener* aListener,
3265
                                          nsIInputStream *aPostStream,
3266
                                          const char *aHeadersData,
3267
                                          uint32_t aHeadersDataLen)
3268
0
{
3269
0
  nsCOMPtr<nsIURI> url;
3270
0
  nsAutoString absUrl;
3271
0
  nsresult rv;
3272
0
3273
0
  if (aURL.Length() <= 0)
3274
0
    return NS_OK;
3275
0
3276
0
  // get the base URI for the plugin to create an absolute url
3277
0
  // in case aURL is relative
3278
0
  RefPtr<nsPluginInstanceOwner> owner = aInstance->GetOwner();
3279
0
  if (owner) {
3280
0
    nsCOMPtr<nsIURI> baseURI = owner->GetBaseURI();
3281
0
    rv = NS_MakeAbsoluteURI(absUrl, aURL, baseURI);
3282
0
  }
3283
0
3284
0
  if (absUrl.IsEmpty())
3285
0
    absUrl.Assign(aURL);
3286
0
3287
0
  rv = NS_NewURI(getter_AddRefs(url), absUrl);
3288
0
  NS_ENSURE_SUCCESS(rv, rv);
3289
0
3290
0
  RefPtr<nsPluginStreamListenerPeer> listenerPeer = new nsPluginStreamListenerPeer();
3291
0
  NS_ENSURE_TRUE(listenerPeer, NS_ERROR_OUT_OF_MEMORY);
3292
0
3293
0
  rv = listenerPeer->Initialize(url, aInstance, aListener);
3294
0
  NS_ENSURE_SUCCESS(rv, rv);
3295
0
3296
0
  RefPtr<dom::Element> element;
3297
0
  nsCOMPtr<nsIDocument> doc;
3298
0
  if (owner) {
3299
0
    owner->GetDOMElement(getter_AddRefs(element));
3300
0
    owner->GetDocument(getter_AddRefs(doc));
3301
0
  }
3302
0
3303
0
  NS_ENSURE_TRUE(element, NS_ERROR_FAILURE);
3304
0
3305
0
  nsCOMPtr<nsIChannel> channel;
3306
0
  // @arg loadgroup:
3307
0
  // do not add this internal plugin's channel on the
3308
0
  // load group otherwise this channel could be canceled
3309
0
  // form |nsDocShell::OnLinkClickSync| bug 166613
3310
0
  rv = NS_NewChannel(getter_AddRefs(channel),
3311
0
                     url,
3312
0
                     element,
3313
0
                     nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS |
3314
0
                     nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL,
3315
0
                     nsIContentPolicy::TYPE_OBJECT_SUBREQUEST,
3316
0
                     nullptr, // aPerformanceStorage
3317
0
                     nullptr,  // aLoadGroup
3318
0
                     listenerPeer,
3319
0
                     nsIRequest::LOAD_NORMAL | nsIChannel::LOAD_CLASSIFY_URI |
3320
0
                     nsIChannel::LOAD_BYPASS_SERVICE_WORKER);
3321
0
  NS_ENSURE_SUCCESS(rv, rv);
3322
0
3323
0
  if (doc) {
3324
0
    // And if it's a script allow it to execute against the
3325
0
    // document's script context.
3326
0
    nsCOMPtr<nsIScriptChannel> scriptChannel(do_QueryInterface(channel));
3327
0
    if (scriptChannel) {
3328
0
      scriptChannel->SetExecutionPolicy(nsIScriptChannel::EXECUTE_NORMAL);
3329
0
      // Plug-ins seem to depend on javascript: URIs running synchronously
3330
0
      scriptChannel->SetExecuteAsync(false);
3331
0
    }
3332
0
  }
3333
0
3334
0
  // deal with headers and post data
3335
0
  nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel));
3336
0
  if (httpChannel) {
3337
0
    if (!aPostStream) {
3338
0
      // Only set the Referer header for GET requests because IIS throws
3339
0
      // errors about malformed requests if we include it in POSTs. See
3340
0
      // bug 724465.
3341
0
      nsCOMPtr<nsIURI> referer;
3342
0
      net::ReferrerPolicy referrerPolicy = net::RP_Unset;
3343
0
3344
0
      nsCOMPtr<nsIObjectLoadingContent> olc = do_QueryInterface(element);
3345
0
      if (olc)
3346
0
        olc->GetSrcURI(getter_AddRefs(referer));
3347
0
3348
0
3349
0
      if (!referer) {
3350
0
        if (!doc) {
3351
0
          return NS_ERROR_FAILURE;
3352
0
        }
3353
0
        referer = doc->GetDocumentURI();
3354
0
        referrerPolicy = doc->GetReferrerPolicy();
3355
0
      }
3356
0
3357
0
      rv = httpChannel->SetReferrerWithPolicy(referer, referrerPolicy);
3358
0
      NS_ENSURE_SUCCESS(rv,rv);
3359
0
    }
3360
0
3361
0
    if (aPostStream) {
3362
0
      // XXX it's a bit of a hack to rewind the postdata stream
3363
0
      // here but it has to be done in case the post data is
3364
0
      // being reused multiple times.
3365
0
      nsCOMPtr<nsISeekableStream>
3366
0
      postDataSeekable(do_QueryInterface(aPostStream));
3367
0
      if (postDataSeekable)
3368
0
        postDataSeekable->Seek(nsISeekableStream::NS_SEEK_SET, 0);
3369
0
3370
0
      nsCOMPtr<nsIUploadChannel> uploadChannel(do_QueryInterface(httpChannel));
3371
0
      NS_ASSERTION(uploadChannel, "http must support nsIUploadChannel");
3372
0
3373
0
      uploadChannel->SetUploadStream(aPostStream, EmptyCString(), -1);
3374
0
    }
3375
0
3376
0
    if (aHeadersData) {
3377
0
      rv = AddHeadersToChannel(aHeadersData, aHeadersDataLen, httpChannel);
3378
0
      NS_ENSURE_SUCCESS(rv,rv);
3379
0
    }
3380
0
  }
3381
0
  rv = channel->AsyncOpen2(listenerPeer);
3382
0
  if (NS_SUCCEEDED(rv))
3383
0
    listenerPeer->TrackRequest(channel);
3384
0
  return rv;
3385
0
}
3386
3387
nsresult
3388
nsPluginHost::AddHeadersToChannel(const char *aHeadersData,
3389
                                  uint32_t aHeadersDataLen,
3390
                                  nsIChannel *aGenericChannel)
3391
0
{
3392
0
  nsresult rv = NS_OK;
3393
0
3394
0
  nsCOMPtr<nsIHttpChannel> aChannel = do_QueryInterface(aGenericChannel);
3395
0
  if (!aChannel) {
3396
0
    return NS_ERROR_NULL_POINTER;
3397
0
  }
3398
0
3399
0
  // used during the manipulation of the String from the aHeadersData
3400
0
  nsAutoCString headersString;
3401
0
  nsAutoCString oneHeader;
3402
0
  nsAutoCString headerName;
3403
0
  nsAutoCString headerValue;
3404
0
  int32_t crlf = 0;
3405
0
  int32_t colon = 0;
3406
0
3407
0
  // Turn the char * buffer into an nsString.
3408
0
  headersString = aHeadersData;
3409
0
3410
0
  // Iterate over the nsString: for each "\r\n" delimited chunk,
3411
0
  // add the value as a header to the nsIHTTPChannel
3412
0
  while (true) {
3413
0
    crlf = headersString.Find("\r\n", true);
3414
0
    if (-1 == crlf) {
3415
0
      rv = NS_OK;
3416
0
      return rv;
3417
0
    }
3418
0
    headersString.Mid(oneHeader, 0, crlf);
3419
0
    headersString.Cut(0, crlf + 2);
3420
0
    oneHeader.StripWhitespace();
3421
0
    colon = oneHeader.Find(":");
3422
0
    if (-1 == colon) {
3423
0
      rv = NS_ERROR_NULL_POINTER;
3424
0
      return rv;
3425
0
    }
3426
0
    oneHeader.Left(headerName, colon);
3427
0
    colon++;
3428
0
    oneHeader.Mid(headerValue, colon, oneHeader.Length() - colon);
3429
0
3430
0
    // FINALLY: we can set the header!
3431
0
3432
0
    rv = aChannel->SetRequestHeader(headerName, headerValue, true);
3433
0
    if (NS_FAILED(rv)) {
3434
0
      rv = NS_ERROR_NULL_POINTER;
3435
0
      return rv;
3436
0
    }
3437
0
  }
3438
0
}
3439
3440
nsresult
3441
nsPluginHost::StopPluginInstance(nsNPAPIPluginInstance* aInstance)
3442
0
{
3443
0
  AUTO_PROFILER_LABEL("nsPluginHost::StopPluginInstance", OTHER);
3444
0
  if (PluginDestructionGuard::DelayDestroy(aInstance)) {
3445
0
    return NS_OK;
3446
0
  }
3447
0
3448
0
  PLUGIN_LOG(PLUGIN_LOG_NORMAL,
3449
0
  ("nsPluginHost::StopPluginInstance called instance=%p\n",aInstance));
3450
0
3451
0
  if (aInstance->HasStartedDestroying()) {
3452
0
    return NS_OK;
3453
0
  }
3454
0
3455
0
  Telemetry::AutoTimer<Telemetry::PLUGIN_SHUTDOWN_MS> timer;
3456
0
  aInstance->Stop();
3457
0
3458
0
  // if the instance does not want to be 'cached' just remove it
3459
0
  bool doCache = aInstance->ShouldCache();
3460
0
  if (doCache) {
3461
0
    // try to get the max cached instances from a pref or use default
3462
0
    uint32_t cachedInstanceLimit =
3463
0
      Preferences::GetUint(NS_PREF_MAX_NUM_CACHED_INSTANCES,
3464
0
                           DEFAULT_NUMBER_OF_STOPPED_INSTANCES);
3465
0
    if (StoppedInstanceCount() >= cachedInstanceLimit) {
3466
0
      nsNPAPIPluginInstance *oldestInstance = FindOldestStoppedInstance();
3467
0
      if (oldestInstance) {
3468
0
        nsPluginTag* pluginTag = TagForPlugin(oldestInstance->GetPlugin());
3469
0
        oldestInstance->Destroy();
3470
0
        mInstances.RemoveElement(oldestInstance);
3471
0
        // TODO: Remove this check once bug 752422 was investigated
3472
0
        if (pluginTag) {
3473
0
          OnPluginInstanceDestroyed(pluginTag);
3474
0
        }
3475
0
      }
3476
0
    }
3477
0
  } else {
3478
0
    nsPluginTag* pluginTag = TagForPlugin(aInstance->GetPlugin());
3479
0
    aInstance->Destroy();
3480
0
    mInstances.RemoveElement(aInstance);
3481
0
    // TODO: Remove this check once bug 752422 was investigated
3482
0
    if (pluginTag) {
3483
0
      OnPluginInstanceDestroyed(pluginTag);
3484
0
    }
3485
0
  }
3486
0
3487
0
  return NS_OK;
3488
0
}
3489
3490
nsresult nsPluginHost::NewPluginStreamListener(nsIURI* aURI,
3491
                                               nsNPAPIPluginInstance* aInstance,
3492
                                               nsIStreamListener **aStreamListener)
3493
0
{
3494
0
  NS_ENSURE_ARG_POINTER(aURI);
3495
0
  NS_ENSURE_ARG_POINTER(aStreamListener);
3496
0
3497
0
  RefPtr<nsPluginStreamListenerPeer> listener = new nsPluginStreamListenerPeer();
3498
0
  nsresult rv = listener->Initialize(aURI, aInstance, nullptr);
3499
0
  if (NS_FAILED(rv)) {
3500
0
    return rv;
3501
0
  }
3502
0
3503
0
  listener.forget(aStreamListener);
3504
0
3505
0
  return NS_OK;
3506
0
}
3507
3508
void nsPluginHost::CreateWidget(nsPluginInstanceOwner* aOwner)
3509
0
{
3510
0
  aOwner->CreateWidget();
3511
0
3512
0
  // If we've got a native window, the let the plugin know about it.
3513
0
  aOwner->CallSetWindow();
3514
0
}
3515
3516
NS_IMETHODIMP nsPluginHost::Observe(nsISupports *aSubject,
3517
                                    const char *aTopic,
3518
                                    const char16_t *someData)
3519
0
{
3520
0
  if (!strcmp(NS_XPCOM_SHUTDOWN_OBSERVER_ID, aTopic)) {
3521
0
    UnloadPlugins();
3522
0
  }
3523
0
  if (!strcmp(NS_PREFBRANCH_PREFCHANGE_TOPIC_ID, aTopic)) {
3524
0
    mPluginsDisabled = Preferences::GetBool("plugin.disable", false);
3525
0
    // Unload or load plugins as needed
3526
0
    if (mPluginsDisabled) {
3527
0
      UnloadPlugins();
3528
0
    } else {
3529
0
      LoadPlugins();
3530
0
    }
3531
0
  }
3532
0
  if (XRE_IsParentProcess() && !strcmp("blocklist-updated", aTopic)) {
3533
0
    // The blocklist has updated. Asynchronously get blocklist state for all items.
3534
0
    // The promise resolution handler takes care of checking if anything changed,
3535
0
    // and writing an updated state to file, as well as sending data to child processes.
3536
0
    nsPluginTag* plugin = mPlugins;
3537
0
    while (plugin) {
3538
0
      UpdatePluginBlocklistState(plugin);
3539
0
      plugin = plugin->mNext;
3540
0
    }
3541
0
  }
3542
0
  return NS_OK;
3543
0
}
3544
3545
nsresult
3546
nsPluginHost::ParsePostBufferToFixHeaders(const char *inPostData, uint32_t inPostDataLen,
3547
                                          char **outPostData, uint32_t *outPostDataLen)
3548
0
{
3549
0
  if (!inPostData || !outPostData || !outPostDataLen)
3550
0
    return NS_ERROR_NULL_POINTER;
3551
0
3552
0
  *outPostData = 0;
3553
0
  *outPostDataLen = 0;
3554
0
3555
0
  const char CR = '\r';
3556
0
  const char LF = '\n';
3557
0
  const char CRLFCRLF[] = {CR,LF,CR,LF,'\0'}; // C string"\r\n\r\n"
3558
0
  const char ContentLenHeader[] = "Content-length";
3559
0
3560
0
  AutoTArray<const char*, 8> singleLF;
3561
0
  const char *pSCntlh = 0;// pointer to start of ContentLenHeader in inPostData
3562
0
  const char *pSod = 0;   // pointer to start of data in inPostData
3563
0
  const char *pEoh = 0;   // pointer to end of headers in inPostData
3564
0
  const char *pEod = inPostData + inPostDataLen; // pointer to end of inPostData
3565
0
  if (*inPostData == LF) {
3566
0
    // If no custom headers are required, simply add a blank
3567
0
    // line ('\n') to the beginning of the file or buffer.
3568
0
    // so *inPostData == '\n' is valid
3569
0
    pSod = inPostData + 1;
3570
0
  } else {
3571
0
    const char *s = inPostData; //tmp pointer to sourse inPostData
3572
0
    while (s < pEod) {
3573
0
      if (!pSCntlh &&
3574
0
          (*s == 'C' || *s == 'c') &&
3575
0
          (s + sizeof(ContentLenHeader) - 1 < pEod) &&
3576
0
          (!PL_strncasecmp(s, ContentLenHeader, sizeof(ContentLenHeader) - 1)))
3577
0
      {
3578
0
        // lets assume this is ContentLenHeader for now
3579
0
        const char *p = pSCntlh = s;
3580
0
        p += sizeof(ContentLenHeader) - 1;
3581
0
        // search for first CR or LF == end of ContentLenHeader
3582
0
        for (; p < pEod; p++) {
3583
0
          if (*p == CR || *p == LF) {
3584
0
            // got delimiter,
3585
0
            // one more check; if previous char is a digit
3586
0
            // most likely pSCntlh points to the start of ContentLenHeader
3587
0
            if (*(p-1) >= '0' && *(p-1) <= '9') {
3588
0
              s = p;
3589
0
            }
3590
0
            break; //for loop
3591
0
          }
3592
0
        }
3593
0
        if (pSCntlh == s) { // curret ptr is the same
3594
0
          pSCntlh = 0; // that was not ContentLenHeader
3595
0
          break; // there is nothing to parse, break *WHILE LOOP* here
3596
0
        }
3597
0
      }
3598
0
3599
0
      if (*s == CR) {
3600
0
        if (pSCntlh && // only if ContentLenHeader is found we are looking for end of headers
3601
0
            ((s + sizeof(CRLFCRLF)-1) <= pEod) &&
3602
0
            !memcmp(s, CRLFCRLF, sizeof(CRLFCRLF)-1))
3603
0
        {
3604
0
          s += sizeof(CRLFCRLF)-1;
3605
0
          pEoh = pSod = s; // data stars here
3606
0
          break;
3607
0
        }
3608
0
      } else if (*s == LF) {
3609
0
        if (*(s-1) != CR) {
3610
0
          singleLF.AppendElement(s);
3611
0
        }
3612
0
        if (pSCntlh && (s+1 < pEod) && (*(s+1) == LF)) {
3613
0
          s++;
3614
0
          singleLF.AppendElement(s);
3615
0
          s++;
3616
0
          pEoh = pSod = s; // data stars here
3617
0
          break;
3618
0
        }
3619
0
      }
3620
0
      s++;
3621
0
    }
3622
0
  }
3623
0
3624
0
  // deal with output buffer
3625
0
  if (!pSod) { // lets assume whole buffer is a data
3626
0
    pSod = inPostData;
3627
0
  }
3628
0
3629
0
  uint32_t newBufferLen = 0;
3630
0
  uint32_t dataLen = pEod - pSod;
3631
0
  uint32_t headersLen = pEoh ? pSod - inPostData : 0;
3632
0
3633
0
  char *p; // tmp ptr into new output buf
3634
0
  if (headersLen) { // we got a headers
3635
0
    // this function does not make any assumption on correctness
3636
0
    // of ContentLenHeader value in this case.
3637
0
3638
0
    newBufferLen = dataLen + headersLen;
3639
0
    // in case there were single LFs in headers
3640
0
    // reserve an extra space for CR will be added before each single LF
3641
0
    int cntSingleLF = singleLF.Length();
3642
0
    newBufferLen += cntSingleLF;
3643
0
3644
0
    *outPostData = p = (char*)moz_xmalloc(newBufferLen);
3645
0
3646
0
    // deal with single LF
3647
0
    const char *s = inPostData;
3648
0
    if (cntSingleLF) {
3649
0
      for (int i=0; i<cntSingleLF; i++) {
3650
0
        const char *plf = singleLF.ElementAt(i); // ptr to single LF in headers
3651
0
        int n = plf - s; // bytes to copy
3652
0
        if (n) { // for '\n\n' there is nothing to memcpy
3653
0
          memcpy(p, s, n);
3654
0
          p += n;
3655
0
        }
3656
0
        *p++ = CR;
3657
0
        s = plf;
3658
0
        *p++ = *s++;
3659
0
      }
3660
0
    }
3661
0
    // are we done with headers?
3662
0
    headersLen = pEoh - s;
3663
0
    if (headersLen) { // not yet
3664
0
      memcpy(p, s, headersLen); // copy the rest
3665
0
      p += headersLen;
3666
0
    }
3667
0
  } else  if (dataLen) { // no ContentLenHeader is found but there is a data
3668
0
    // make new output buffer big enough
3669
0
    // to keep ContentLenHeader+value followed by data
3670
0
    uint32_t l = sizeof(ContentLenHeader) + sizeof(CRLFCRLF) + 32;
3671
0
    newBufferLen = dataLen + l;
3672
0
    *outPostData = p = (char*)moz_xmalloc(newBufferLen);
3673
0
    headersLen = snprintf(p, l,"%s: %u%s", ContentLenHeader, dataLen, CRLFCRLF);
3674
0
    if (headersLen == l) { // if snprintf has ate all extra space consider this as an error
3675
0
      free(p);
3676
0
      *outPostData = 0;
3677
0
      return NS_ERROR_FAILURE;
3678
0
    }
3679
0
    p += headersLen;
3680
0
    newBufferLen = headersLen + dataLen;
3681
0
  }
3682
0
  // at this point we've done with headers.
3683
0
  // there is a possibility that input buffer has only headers info in it
3684
0
  // which already parsed and copied into output buffer.
3685
0
  // copy the data
3686
0
  if (dataLen) {
3687
0
    memcpy(p, pSod, dataLen);
3688
0
  }
3689
0
3690
0
  *outPostDataLen = newBufferLen;
3691
0
3692
0
  return NS_OK;
3693
0
}
3694
3695
nsresult
3696
nsPluginHost::NewPluginNativeWindow(nsPluginNativeWindow ** aPluginNativeWindow)
3697
0
{
3698
0
  return PLUG_NewPluginNativeWindow(aPluginNativeWindow);
3699
0
}
3700
3701
nsresult
3702
nsPluginHost::GetPluginName(nsNPAPIPluginInstance *aPluginInstance,
3703
                            const char** aPluginName)
3704
0
{
3705
0
  nsNPAPIPluginInstance *instance = static_cast<nsNPAPIPluginInstance*>(aPluginInstance);
3706
0
  if (!instance)
3707
0
    return NS_ERROR_FAILURE;
3708
0
3709
0
  nsNPAPIPlugin* plugin = instance->GetPlugin();
3710
0
  if (!plugin)
3711
0
    return NS_ERROR_FAILURE;
3712
0
3713
0
  *aPluginName = TagForPlugin(plugin)->Name().get();
3714
0
3715
0
  return NS_OK;
3716
0
}
3717
3718
nsresult
3719
nsPluginHost::GetPluginTagForInstance(nsNPAPIPluginInstance *aPluginInstance,
3720
                                      nsIPluginTag **aPluginTag)
3721
0
{
3722
0
  NS_ENSURE_ARG_POINTER(aPluginInstance);
3723
0
  NS_ENSURE_ARG_POINTER(aPluginTag);
3724
0
3725
0
  nsNPAPIPlugin *plugin = aPluginInstance->GetPlugin();
3726
0
  if (!plugin)
3727
0
    return NS_ERROR_FAILURE;
3728
0
3729
0
  *aPluginTag = TagForPlugin(plugin);
3730
0
3731
0
  NS_ADDREF(*aPluginTag);
3732
0
  return NS_OK;
3733
0
}
3734
3735
NS_IMETHODIMP nsPluginHost::Notify(nsITimer* timer)
3736
0
{
3737
0
  RefPtr<nsPluginTag> pluginTag = mPlugins;
3738
0
  while (pluginTag) {
3739
0
    if (pluginTag->mUnloadTimer == timer) {
3740
0
      if (!IsRunningPlugin(pluginTag)) {
3741
0
        pluginTag->TryUnloadPlugin(false);
3742
0
      }
3743
0
      return NS_OK;
3744
0
    }
3745
0
    pluginTag = pluginTag->mNext;
3746
0
  }
3747
0
3748
0
  return NS_ERROR_FAILURE;
3749
0
}
3750
3751
NS_IMETHODIMP
3752
nsPluginHost::GetName(nsACString& aName)
3753
0
{
3754
0
  aName.AssignLiteral("nsPluginHost");
3755
0
  return NS_OK;
3756
0
}
3757
3758
#ifdef XP_WIN
3759
// Re-enable any top level browser windows that were disabled by modal dialogs
3760
// displayed by the crashed plugin.
3761
static void
3762
CheckForDisabledWindows()
3763
{
3764
  nsCOMPtr<nsIWindowMediator> wm(do_GetService(NS_WINDOWMEDIATOR_CONTRACTID));
3765
  if (!wm)
3766
    return;
3767
3768
  nsCOMPtr<nsISimpleEnumerator> windowList;
3769
  wm->GetXULWindowEnumerator(nullptr, getter_AddRefs(windowList));
3770
  if (!windowList)
3771
    return;
3772
3773
  bool haveWindows;
3774
  do {
3775
    windowList->HasMoreElements(&haveWindows);
3776
    if (!haveWindows)
3777
      return;
3778
3779
    nsCOMPtr<nsISupports> supportsWindow;
3780
    windowList->GetNext(getter_AddRefs(supportsWindow));
3781
    nsCOMPtr<nsIBaseWindow> baseWin(do_QueryInterface(supportsWindow));
3782
    if (baseWin) {
3783
      nsCOMPtr<nsIWidget> widget;
3784
      baseWin->GetMainWidget(getter_AddRefs(widget));
3785
      if (widget && !widget->GetParent() &&
3786
          widget->IsVisible() &&
3787
          !widget->IsEnabled()) {
3788
        nsIWidget* child = widget->GetFirstChild();
3789
        bool enable = true;
3790
        while (child)  {
3791
          if (child->WindowType() == eWindowType_dialog) {
3792
            enable = false;
3793
            break;
3794
          }
3795
          child = child->GetNextSibling();
3796
        }
3797
        if (enable) {
3798
          widget->Enable(true);
3799
        }
3800
      }
3801
    }
3802
  } while (haveWindows);
3803
}
3804
#endif
3805
3806
void
3807
nsPluginHost::PluginCrashed(nsNPAPIPlugin* aPlugin,
3808
                            const nsAString& pluginDumpID,
3809
                            const nsAString& browserDumpID)
3810
0
{
3811
0
  nsPluginTag* crashedPluginTag = TagForPlugin(aPlugin);
3812
0
  MOZ_ASSERT(crashedPluginTag);
3813
0
3814
0
  // Notify the app's observer that a plugin crashed so it can submit
3815
0
  // a crashreport.
3816
0
  bool submittedCrashReport = false;
3817
0
  nsCOMPtr<nsIObserverService> obsService =
3818
0
    mozilla::services::GetObserverService();
3819
0
  nsCOMPtr<nsIWritablePropertyBag2> propbag =
3820
0
    do_CreateInstance("@mozilla.org/hash-property-bag;1");
3821
0
  if (obsService && propbag) {
3822
0
    uint32_t runID = 0;
3823
0
    PluginLibrary* library = aPlugin->GetLibrary();
3824
0
3825
0
    if (!NS_WARN_IF(!library)) {
3826
0
      library->GetRunID(&runID);
3827
0
    }
3828
0
    propbag->SetPropertyAsUint32(NS_LITERAL_STRING("runID"), runID);
3829
0
3830
0
    nsCString pluginName;
3831
0
    crashedPluginTag->GetName(pluginName);
3832
0
    propbag->SetPropertyAsAString(NS_LITERAL_STRING("pluginName"),
3833
0
                                   NS_ConvertUTF8toUTF16(pluginName));
3834
0
    propbag->SetPropertyAsAString(NS_LITERAL_STRING("pluginDumpID"),
3835
0
                                  pluginDumpID);
3836
0
    propbag->SetPropertyAsAString(NS_LITERAL_STRING("browserDumpID"),
3837
0
                                  browserDumpID);
3838
0
    propbag->SetPropertyAsBool(NS_LITERAL_STRING("submittedCrashReport"),
3839
0
                               submittedCrashReport);
3840
0
    obsService->NotifyObservers(propbag, "plugin-crashed", nullptr);
3841
0
    // see if an observer submitted a crash report.
3842
0
    propbag->GetPropertyAsBool(NS_LITERAL_STRING("submittedCrashReport"),
3843
0
                               &submittedCrashReport);
3844
0
  }
3845
0
3846
0
  // Invalidate each nsPluginInstanceTag for the crashed plugin
3847
0
3848
0
  for (uint32_t i = mInstances.Length(); i > 0; i--) {
3849
0
    nsNPAPIPluginInstance* instance = mInstances[i - 1];
3850
0
    if (instance->GetPlugin() == aPlugin) {
3851
0
      // notify the content node (nsIObjectLoadingContent) that the
3852
0
      // plugin has crashed
3853
0
      RefPtr<dom::Element> domElement;
3854
0
      instance->GetDOMElement(getter_AddRefs(domElement));
3855
0
      nsCOMPtr<nsIObjectLoadingContent> objectContent(do_QueryInterface(domElement));
3856
0
      if (objectContent) {
3857
0
        objectContent->PluginCrashed(crashedPluginTag, pluginDumpID, browserDumpID,
3858
0
                                     submittedCrashReport);
3859
0
      }
3860
0
3861
0
      instance->Destroy();
3862
0
      mInstances.RemoveElement(instance);
3863
0
      OnPluginInstanceDestroyed(crashedPluginTag);
3864
0
    }
3865
0
  }
3866
0
3867
0
  // Only after all instances have been invalidated is it safe to null
3868
0
  // out nsPluginTag.mPlugin. The next time we try to create an
3869
0
  // instance of this plugin we reload it (launch a new plugin process).
3870
0
3871
0
  crashedPluginTag->mPlugin = nullptr;
3872
0
  crashedPluginTag->mContentProcessRunningCount = 0;
3873
0
3874
#ifdef XP_WIN
3875
  CheckForDisabledWindows();
3876
#endif
3877
}
3878
3879
nsNPAPIPluginInstance*
3880
nsPluginHost::FindInstance(const char *mimetype)
3881
0
{
3882
0
  for (uint32_t i = 0; i < mInstances.Length(); i++) {
3883
0
    nsNPAPIPluginInstance* instance = mInstances[i];
3884
0
3885
0
    const char* mt;
3886
0
    nsresult rv = instance->GetMIMEType(&mt);
3887
0
    if (NS_FAILED(rv))
3888
0
      continue;
3889
0
3890
0
    if (PL_strcasecmp(mt, mimetype) == 0)
3891
0
      return instance;
3892
0
  }
3893
0
3894
0
  return nullptr;
3895
0
}
3896
3897
nsNPAPIPluginInstance*
3898
nsPluginHost::FindOldestStoppedInstance()
3899
0
{
3900
0
  nsNPAPIPluginInstance *oldestInstance = nullptr;
3901
0
  TimeStamp oldestTime = TimeStamp::Now();
3902
0
  for (uint32_t i = 0; i < mInstances.Length(); i++) {
3903
0
    nsNPAPIPluginInstance *instance = mInstances[i];
3904
0
    if (instance->IsRunning())
3905
0
      continue;
3906
0
3907
0
    TimeStamp time = instance->StopTime();
3908
0
    if (time < oldestTime) {
3909
0
      oldestTime = time;
3910
0
      oldestInstance = instance;
3911
0
    }
3912
0
  }
3913
0
3914
0
  return oldestInstance;
3915
0
}
3916
3917
uint32_t
3918
nsPluginHost::StoppedInstanceCount()
3919
0
{
3920
0
  uint32_t stoppedCount = 0;
3921
0
  for (uint32_t i = 0; i < mInstances.Length(); i++) {
3922
0
    nsNPAPIPluginInstance *instance = mInstances[i];
3923
0
    if (!instance->IsRunning())
3924
0
      stoppedCount++;
3925
0
  }
3926
0
  return stoppedCount;
3927
0
}
3928
3929
nsTArray< RefPtr<nsNPAPIPluginInstance> >*
3930
nsPluginHost::InstanceArray()
3931
0
{
3932
0
  return &mInstances;
3933
0
}
3934
3935
void
3936
nsPluginHost::DestroyRunningInstances(nsPluginTag* aPluginTag)
3937
0
{
3938
0
  for (int32_t i = mInstances.Length(); i > 0; i--) {
3939
0
    nsNPAPIPluginInstance *instance = mInstances[i - 1];
3940
0
    if (instance->IsRunning() && (!aPluginTag || aPluginTag == TagForPlugin(instance->GetPlugin()))) {
3941
0
      instance->SetWindow(nullptr);
3942
0
      instance->Stop();
3943
0
3944
0
      // Get rid of all the instances without the possibility of caching.
3945
0
      nsPluginTag* pluginTag = TagForPlugin(instance->GetPlugin());
3946
0
      instance->SetWindow(nullptr);
3947
0
3948
0
      RefPtr<dom::Element> domElement;
3949
0
      instance->GetDOMElement(getter_AddRefs(domElement));
3950
0
      nsCOMPtr<nsIObjectLoadingContent> objectContent =
3951
0
        do_QueryInterface(domElement);
3952
0
3953
0
      instance->Destroy();
3954
0
3955
0
      mInstances.RemoveElement(instance);
3956
0
      OnPluginInstanceDestroyed(pluginTag);
3957
0
3958
0
      // Notify owning content that we destroyed its plugin out from under it
3959
0
      if (objectContent) {
3960
0
        objectContent->PluginDestroyed();
3961
0
      }
3962
0
    }
3963
0
  }
3964
0
}
3965
3966
/* static */
3967
bool
3968
nsPluginHost::CanUsePluginForMIMEType(const nsACString& aMIMEType)
3969
0
{
3970
0
  // We only support flash as a plugin, so if the mime types don't match for
3971
0
  // those, exit before we start loading plugins.
3972
0
  //
3973
0
  // XXX: Remove test/java cases when bug 1351885 lands.
3974
0
  if (nsPluginHost::GetSpecialType(aMIMEType) == nsPluginHost::eSpecialType_Flash ||
3975
0
      MimeTypeIsAllowedForFakePlugin(NS_ConvertUTF8toUTF16(aMIMEType)) ||
3976
0
      aMIMEType.LowerCaseEqualsLiteral("application/x-test") ||
3977
0
      aMIMEType.LowerCaseEqualsLiteral("application/x-second-test") ||
3978
0
      aMIMEType.LowerCaseEqualsLiteral("application/x-third-test")) {
3979
0
    return true;
3980
0
  }
3981
0
3982
0
  return false;
3983
0
}
3984
3985
// Runnable that does an async destroy of a plugin.
3986
3987
class nsPluginDestroyRunnable : public Runnable,
3988
                                public mozilla::LinkedListElement<nsPluginDestroyRunnable>
3989
{
3990
public:
3991
  explicit nsPluginDestroyRunnable(nsNPAPIPluginInstance *aInstance)
3992
    : Runnable("nsPluginDestroyRunnable"),
3993
      mInstance(aInstance)
3994
0
  {
3995
0
    sRunnableList.insertBack(this);
3996
0
  }
3997
3998
  ~nsPluginDestroyRunnable() override
3999
0
  {
4000
0
    this->remove();
4001
0
  }
4002
4003
  NS_IMETHOD Run() override
4004
0
  {
4005
0
    RefPtr<nsNPAPIPluginInstance> instance;
4006
0
4007
0
    // Null out mInstance to make sure this code in another runnable
4008
0
    // will do the right thing even if someone was holding on to this
4009
0
    // runnable longer than we expect.
4010
0
    instance.swap(mInstance);
4011
0
4012
0
    if (PluginDestructionGuard::DelayDestroy(instance)) {
4013
0
      // It's still not safe to destroy the plugin, it's now up to the
4014
0
      // outermost guard on the stack to take care of the destruction.
4015
0
      return NS_OK;
4016
0
    }
4017
0
4018
0
    for (auto r : sRunnableList) {
4019
0
      if (r != this && r->mInstance == instance) {
4020
0
        // There's another runnable scheduled to tear down
4021
0
        // instance. Let it do the job.
4022
0
        return NS_OK;
4023
0
      }
4024
0
    }
4025
0
4026
0
    PLUGIN_LOG(PLUGIN_LOG_NORMAL,
4027
0
               ("Doing delayed destroy of instance %p\n", instance.get()));
4028
0
4029
0
    RefPtr<nsPluginHost> host = nsPluginHost::GetInst();
4030
0
    if (host)
4031
0
      host->StopPluginInstance(instance);
4032
0
4033
0
    PLUGIN_LOG(PLUGIN_LOG_NORMAL,
4034
0
               ("Done with delayed destroy of instance %p\n", instance.get()));
4035
0
4036
0
    return NS_OK;
4037
0
  }
4038
4039
protected:
4040
  RefPtr<nsNPAPIPluginInstance> mInstance;
4041
4042
  static mozilla::LinkedList<nsPluginDestroyRunnable> sRunnableList;
4043
};
4044
4045
mozilla::LinkedList<nsPluginDestroyRunnable> nsPluginDestroyRunnable::sRunnableList;
4046
4047
mozilla::LinkedList<PluginDestructionGuard> PluginDestructionGuard::sList;
4048
4049
PluginDestructionGuard::PluginDestructionGuard(nsNPAPIPluginInstance *aInstance)
4050
  : mInstance(aInstance)
4051
0
{
4052
0
  Init();
4053
0
}
4054
4055
PluginDestructionGuard::PluginDestructionGuard(NPP npp)
4056
  : mInstance(npp ? static_cast<nsNPAPIPluginInstance*>(npp->ndata) : nullptr)
4057
0
{
4058
0
  Init();
4059
0
}
4060
4061
PluginDestructionGuard::~PluginDestructionGuard()
4062
0
{
4063
0
  NS_ASSERTION(NS_IsMainThread(), "Should be on the main thread");
4064
0
4065
0
  this->remove();
4066
0
4067
0
  if (mDelayedDestroy) {
4068
0
    // We've attempted to destroy the plugin instance we're holding on
4069
0
    // to while we were guarding it. Do the actual destroy now, off of
4070
0
    // a runnable.
4071
0
    RefPtr<nsPluginDestroyRunnable> evt =
4072
0
      new nsPluginDestroyRunnable(mInstance);
4073
0
4074
0
    NS_DispatchToMainThread(evt);
4075
0
  }
4076
0
}
4077
4078
// static
4079
bool
4080
PluginDestructionGuard::DelayDestroy(nsNPAPIPluginInstance *aInstance)
4081
0
{
4082
0
  NS_ASSERTION(NS_IsMainThread(), "Should be on the main thread");
4083
0
  NS_ASSERTION(aInstance, "Uh, I need an instance!");
4084
0
4085
0
  // Find the first guard on the stack and make it do a delayed
4086
0
  // destroy upon destruction.
4087
0
4088
0
  for (auto g : sList) {
4089
0
    if (g->mInstance == aInstance) {
4090
0
      g->mDelayedDestroy = true;
4091
0
4092
0
      return true;
4093
0
    }
4094
0
  }
4095
0
4096
0
  return false;
4097
0
}