dom/plugins/base/nsPluginHost.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     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/. */
     6 /* nsPluginHost.cpp - top-level plugin management code */
     8 #include "nscore.h"
     9 #include "nsPluginHost.h"
    11 #include <cstdlib>
    12 #include <stdio.h>
    13 #include "prio.h"
    14 #include "prmem.h"
    15 #include "nsNPAPIPlugin.h"
    16 #include "nsNPAPIPluginStreamListener.h"
    17 #include "nsNPAPIPluginInstance.h"
    18 #include "nsPluginInstanceOwner.h"
    19 #include "nsObjectLoadingContent.h"
    20 #include "nsIHTTPHeaderListener.h"
    21 #include "nsIHttpHeaderVisitor.h"
    22 #include "nsIObserverService.h"
    23 #include "nsIHttpProtocolHandler.h"
    24 #include "nsIHttpChannel.h"
    25 #include "nsIUploadChannel.h"
    26 #include "nsIByteRangeRequest.h"
    27 #include "nsIStreamListener.h"
    28 #include "nsIInputStream.h"
    29 #include "nsIOutputStream.h"
    30 #include "nsIURL.h"
    31 #include "nsTArray.h"
    32 #include "nsReadableUtils.h"
    33 #include "nsProtocolProxyService.h"
    34 #include "nsIStreamConverterService.h"
    35 #include "nsIFile.h"
    36 #if defined(XP_MACOSX)
    37 #include "nsILocalFileMac.h"
    38 #endif
    39 #include "nsISeekableStream.h"
    40 #include "nsNetUtil.h"
    41 #include "nsIProgressEventSink.h"
    42 #include "nsIDocument.h"
    43 #include "nsPluginLogging.h"
    44 #include "nsIScriptChannel.h"
    45 #include "nsIBlocklistService.h"
    46 #include "nsVersionComparator.h"
    47 #include "nsIObjectLoadingContent.h"
    48 #include "nsIWritablePropertyBag2.h"
    49 #include "nsICategoryManager.h"
    50 #include "nsPluginStreamListenerPeer.h"
    51 #include "mozilla/Preferences.h"
    53 #include "nsEnumeratorUtils.h"
    54 #include "nsXPCOM.h"
    55 #include "nsXPCOMCID.h"
    56 #include "nsISupportsPrimitives.h"
    58 #include "nsXULAppAPI.h"
    59 #include "nsIXULRuntime.h"
    61 // for the dialog
    62 #include "nsIWindowWatcher.h"
    63 #include "nsIDOMElement.h"
    64 #include "nsIDOMWindow.h"
    66 #include "nsNetCID.h"
    67 #include "prprf.h"
    68 #include "nsThreadUtils.h"
    69 #include "nsIInputStreamTee.h"
    71 #include "nsDirectoryServiceDefs.h"
    72 #include "nsAppDirectoryServiceDefs.h"
    73 #include "nsPluginDirServiceProvider.h"
    75 #include "nsUnicharUtils.h"
    76 #include "nsPluginManifestLineReader.h"
    78 #include "nsIWeakReferenceUtils.h"
    79 #include "nsIPresShell.h"
    80 #include "nsPluginNativeWindow.h"
    81 #include "nsIScriptSecurityManager.h"
    82 #include "nsIContentPolicy.h"
    83 #include "nsContentPolicyUtils.h"
    84 #include "mozilla/TimeStamp.h"
    85 #include "mozilla/Telemetry.h"
    86 #include "nsIImageLoadingContent.h"
    87 #include "mozilla/Preferences.h"
    88 #include "nsVersionComparator.h"
    90 #if defined(XP_WIN)
    91 #include "nsIWindowMediator.h"
    92 #include "nsIBaseWindow.h"
    93 #include "windows.h"
    94 #include "winbase.h"
    95 #endif
    97 #ifdef ANDROID
    98 #include <android/log.h>
    99 #define LOG(args...)  __android_log_print(ANDROID_LOG_INFO, "GeckoPlugins" , ## args)
   100 #endif
   102 #if MOZ_CRASHREPORTER
   103 #include "nsExceptionHandler.h"
   104 #endif
   106 using namespace mozilla;
   107 using mozilla::TimeStamp;
   109 // Null out a strong ref to a linked list iteratively to avoid
   110 // exhausting the stack (bug 486349).
   111 #define NS_ITERATIVE_UNREF_LIST(type_, list_, mNext_)                \
   112   {                                                                  \
   113     while (list_) {                                                  \
   114       type_ temp = list_->mNext_;                                    \
   115       list_->mNext_ = nullptr;                                        \
   116       list_ = temp;                                                  \
   117     }                                                                \
   118   }
   120 // this is the name of the directory which will be created
   121 // to cache temporary files.
   122 #define kPluginTmpDirName NS_LITERAL_CSTRING("plugtmp")
   124 static const char *kPrefWhitelist = "plugin.allowed_types";
   125 static const char *kPrefDisableFullPage = "plugin.disable_full_page_plugin_for_types";
   126 static const char *kPrefJavaMIME = "plugin.java.mime";
   128 // Version of cached plugin info
   129 // 0.01 first implementation
   130 // 0.02 added caching of CanUnload to fix bug 105935
   131 // 0.03 changed name, description and mime desc from string to bytes, bug 108246
   132 // 0.04 added new mime entry point on Mac, bug 113464
   133 // 0.05 added new entry point check for the default plugin, bug 132430
   134 // 0.06 strip off suffixes in mime description strings, bug 53895
   135 // 0.07 changed nsIRegistry to flat file support for caching plugins info
   136 // 0.08 mime entry point on MachO, bug 137535
   137 // 0.09 the file encoding is changed to UTF-8, bug 420285
   138 // 0.10 added plugin versions on appropriate platforms, bug 427743
   139 // 0.11 file name and full path fields now store expected values on all platforms, bug 488181
   140 // 0.12 force refresh due to quicktime pdf claim fix, bug 611197
   141 // 0.13 add architecture and list of invalid plugins, bug 616271
   142 // 0.14 force refresh due to locale comparison fix, bug 611296
   143 // 0.15 force refresh due to bug in reading Java plist MIME data, bug 638171
   144 // 0.16 version bump to avoid importing the plugin flags in newer versions
   145 // 0.17 added flag on whether plugin is loaded from an XPI
   146 // The current plugin registry version (and the maximum version we know how to read)
   147 static const char *kPluginRegistryVersion = "0.17";
   148 // The minimum registry version we know how to read
   149 static const char *kMinimumRegistryVersion = "0.9";
   151 static const char kDirectoryServiceContractID[] = "@mozilla.org/file/directory_service;1";
   153 #define kPluginRegistryFilename NS_LITERAL_CSTRING("pluginreg.dat")
   155 #ifdef PLUGIN_LOGGING
   156 PRLogModuleInfo* nsPluginLogging::gNPNLog = nullptr;
   157 PRLogModuleInfo* nsPluginLogging::gNPPLog = nullptr;
   158 PRLogModuleInfo* nsPluginLogging::gPluginLog = nullptr;
   159 #endif
   161 // #defines for plugin cache and prefs
   162 #define NS_PREF_MAX_NUM_CACHED_INSTANCES "browser.plugins.max_num_cached_plugins"
   163 // Raise this from '10' to '50' to work around a bug in Apple's current Java
   164 // plugins on OS X Lion and SnowLeopard.  See bug 705931.
   165 #define DEFAULT_NUMBER_OF_STOPPED_INSTANCES 50
   167 nsIFile *nsPluginHost::sPluginTempDir;
   168 nsPluginHost *nsPluginHost::sInst;
   170 NS_IMPL_ISUPPORTS0(nsInvalidPluginTag)
   172 nsInvalidPluginTag::nsInvalidPluginTag(const char* aFullPath, int64_t aLastModifiedTime)
   173 : mFullPath(aFullPath),
   174   mLastModifiedTime(aLastModifiedTime),
   175   mSeen(false)
   176 {}
   178 nsInvalidPluginTag::~nsInvalidPluginTag()
   179 {}
   181 // Helper to check for a MIME in a comma-delimited preference
   182 static bool
   183 IsTypeInList(nsCString &aMimeType, nsCString aTypeList)
   184 {
   185   nsAutoCString searchStr;
   186   searchStr.Assign(',');
   187   searchStr.Append(aTypeList);
   188   searchStr.Append(',');
   190   nsACString::const_iterator start, end;
   192   searchStr.BeginReading(start);
   193   searchStr.EndReading(end);
   195   nsAutoCString commaSeparated;
   196   commaSeparated.Assign(',');
   197   commaSeparated += aMimeType;
   198   commaSeparated.Append(',');
   200   return FindInReadable(commaSeparated, start, end);
   201 }
   203 // flat file reg funcs
   204 static
   205 bool ReadSectionHeader(nsPluginManifestLineReader& reader, const char *token)
   206 {
   207   do {
   208     if (*reader.LinePtr() == '[') {
   209       char* p = reader.LinePtr() + (reader.LineLength() - 1);
   210       if (*p != ']')
   211         break;
   212       *p = 0;
   214       char* values[1];
   215       if (1 != reader.ParseLine(values, 1))
   216         break;
   217       // ignore the leading '['
   218       if (PL_strcmp(values[0]+1, token)) {
   219         break; // it's wrong token
   220       }
   221       return true;
   222     }
   223   } while (reader.NextLine());
   224   return false;
   225 }
   227 static bool UnloadPluginsASAP()
   228 {
   229   return Preferences::GetBool("dom.ipc.plugins.unloadASAP", false);
   230 }
   232 nsPluginHost::nsPluginHost()
   233   // No need to initialize members to nullptr, false etc because this class
   234   // has a zeroing operator new.
   235 {
   236   // check to see if pref is set at startup to let plugins take over in
   237   // full page mode for certain image mime types that we handle internally
   238   mOverrideInternalTypes =
   239     Preferences::GetBool("plugin.override_internal_types", false);
   241   mPluginsDisabled = Preferences::GetBool("plugin.disable", false);
   242   mPluginsClickToPlay = Preferences::GetBool("plugins.click_to_play", false);
   244   Preferences::AddStrongObserver(this, "plugin.disable");
   245   Preferences::AddStrongObserver(this, "plugins.click_to_play");
   247   nsCOMPtr<nsIObserverService> obsService =
   248     mozilla::services::GetObserverService();
   249   if (obsService) {
   250     obsService->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
   251     obsService->AddObserver(this, "blocklist-updated", false);
   252 #ifdef MOZ_WIDGET_ANDROID
   253     obsService->AddObserver(this, "application-foreground", false);
   254     obsService->AddObserver(this, "application-background", false);
   255 #endif
   256   }
   258 #ifdef PLUGIN_LOGGING
   259   nsPluginLogging::gNPNLog = PR_NewLogModule(NPN_LOG_NAME);
   260   nsPluginLogging::gNPPLog = PR_NewLogModule(NPP_LOG_NAME);
   261   nsPluginLogging::gPluginLog = PR_NewLogModule(PLUGIN_LOG_NAME);
   263   PR_LOG(nsPluginLogging::gNPNLog, PLUGIN_LOG_ALWAYS,("NPN Logging Active!\n"));
   264   PR_LOG(nsPluginLogging::gPluginLog, PLUGIN_LOG_ALWAYS,("General Plugin Logging Active! (nsPluginHost::ctor)\n"));
   265   PR_LOG(nsPluginLogging::gNPPLog, PLUGIN_LOG_ALWAYS,("NPP Logging Active!\n"));
   267   PLUGIN_LOG(PLUGIN_LOG_ALWAYS,("nsPluginHost::ctor\n"));
   268   PR_LogFlush();
   269 #endif
   270 }
   272 nsPluginHost::~nsPluginHost()
   273 {
   274   PLUGIN_LOG(PLUGIN_LOG_ALWAYS,("nsPluginHost::dtor\n"));
   276   UnloadPlugins();
   277   sInst = nullptr;
   278 }
   280 NS_IMPL_ISUPPORTS(nsPluginHost,
   281                   nsIPluginHost,
   282                   nsIObserver,
   283                   nsITimerCallback,
   284                   nsISupportsWeakReference)
   286 already_AddRefed<nsPluginHost>
   287 nsPluginHost::GetInst()
   288 {
   289   if (!sInst) {
   290     sInst = new nsPluginHost();
   291     if (!sInst)
   292       return nullptr;
   293     NS_ADDREF(sInst);
   294   }
   296   nsRefPtr<nsPluginHost> inst = sInst;
   297   return inst.forget();
   298 }
   300 bool nsPluginHost::IsRunningPlugin(nsPluginTag * aPluginTag)
   301 {
   302   if (!aPluginTag || !aPluginTag->mPlugin) {
   303     return false;
   304   }
   306   for (uint32_t i = 0; i < mInstances.Length(); i++) {
   307     nsNPAPIPluginInstance *instance = mInstances[i].get();
   308     if (instance &&
   309         instance->GetPlugin() == aPluginTag->mPlugin &&
   310         instance->IsRunning()) {
   311       return true;
   312     }
   313   }
   315   return false;
   316 }
   318 nsresult nsPluginHost::ReloadPlugins()
   319 {
   320   PLUGIN_LOG(PLUGIN_LOG_NORMAL,
   321   ("nsPluginHost::ReloadPlugins Begin\n"));
   323   nsresult rv = NS_OK;
   325   // this will create the initial plugin list out of cache
   326   // if it was not created yet
   327   if (!mPluginsLoaded)
   328     return LoadPlugins();
   330   // we are re-scanning plugins. New plugins may have been added, also some
   331   // plugins may have been removed, so we should probably shut everything down
   332   // but don't touch running (active and not stopped) plugins
   334   // check if plugins changed, no need to do anything else
   335   // if no changes to plugins have been made
   336   // false instructs not to touch the plugin list, just to
   337   // look for possible changes
   338   bool pluginschanged = true;
   339   FindPlugins(false, &pluginschanged);
   341   // if no changed detected, return an appropriate error code
   342   if (!pluginschanged)
   343     return NS_ERROR_PLUGINS_PLUGINSNOTCHANGED;
   345   // shutdown plugins and kill the list if there are no running plugins
   346   nsRefPtr<nsPluginTag> prev;
   347   nsRefPtr<nsPluginTag> next;
   349   for (nsRefPtr<nsPluginTag> p = mPlugins; p != nullptr;) {
   350     next = p->mNext;
   352     // only remove our plugin from the list if it's not running.
   353     if (!IsRunningPlugin(p)) {
   354       if (p == mPlugins)
   355         mPlugins = next;
   356       else
   357         prev->mNext = next;
   359       p->mNext = nullptr;
   361       // attempt to unload plugins whenever they are removed from the list
   362       p->TryUnloadPlugin(false);
   364       p = next;
   365       continue;
   366     }
   368     prev = p;
   369     p = next;
   370   }
   372   // set flags
   373   mPluginsLoaded = false;
   375   // load them again
   376   rv = LoadPlugins();
   378   PLUGIN_LOG(PLUGIN_LOG_NORMAL,
   379   ("nsPluginHost::ReloadPlugins End\n"));
   381   return rv;
   382 }
   384 #define NS_RETURN_UASTRING_SIZE 128
   386 nsresult nsPluginHost::UserAgent(const char **retstring)
   387 {
   388   static char resultString[NS_RETURN_UASTRING_SIZE];
   389   nsresult res;
   391   nsCOMPtr<nsIHttpProtocolHandler> http = do_GetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "http", &res);
   392   if (NS_FAILED(res))
   393     return res;
   395   nsAutoCString uaString;
   396   res = http->GetUserAgent(uaString);
   398   if (NS_SUCCEEDED(res)) {
   399     if (NS_RETURN_UASTRING_SIZE > uaString.Length()) {
   400       PL_strcpy(resultString, uaString.get());
   401     } else {
   402       // Copy as much of UA string as we can (terminate at right-most space).
   403       PL_strncpy(resultString, uaString.get(), NS_RETURN_UASTRING_SIZE);
   404       for (int i = NS_RETURN_UASTRING_SIZE - 1; i >= 0; i--) {
   405         if (i == 0) {
   406           resultString[NS_RETURN_UASTRING_SIZE - 1] = '\0';
   407         }
   408         else if (resultString[i] == ' ') {
   409           resultString[i] = '\0';
   410           break;
   411         }
   412       }
   413     }
   414     *retstring = resultString;
   415   }
   416   else {
   417     *retstring = nullptr;
   418   }
   420   PLUGIN_LOG(PLUGIN_LOG_NORMAL, ("nsPluginHost::UserAgent return=%s\n", *retstring));
   422   return res;
   423 }
   425 nsresult nsPluginHost::GetURL(nsISupports* pluginInst,
   426                               const char* url,
   427                               const char* target,
   428                               nsNPAPIPluginStreamListener* streamListener,
   429                               const char* altHost,
   430                               const char* referrer,
   431                               bool forceJSEnabled)
   432 {
   433   return GetURLWithHeaders(static_cast<nsNPAPIPluginInstance*>(pluginInst),
   434                            url, target, streamListener, altHost, referrer,
   435                            forceJSEnabled, 0, nullptr);
   436 }
   438 nsresult nsPluginHost::GetURLWithHeaders(nsNPAPIPluginInstance* pluginInst,
   439                                          const char* url,
   440                                          const char* target,
   441                                          nsNPAPIPluginStreamListener* streamListener,
   442                                          const char* altHost,
   443                                          const char* referrer,
   444                                          bool forceJSEnabled,
   445                                          uint32_t getHeadersLength,
   446                                          const char* getHeaders)
   447 {
   448   // we can only send a stream back to the plugin (as specified by a
   449   // null target) if we also have a nsNPAPIPluginStreamListener to talk to
   450   if (!target && !streamListener)
   451     return NS_ERROR_ILLEGAL_VALUE;
   453   nsresult rv = DoURLLoadSecurityCheck(pluginInst, url);
   454   if (NS_FAILED(rv))
   455     return rv;
   457   if (target) {
   458     nsRefPtr<nsPluginInstanceOwner> owner = pluginInst->GetOwner();
   459     if (owner) {
   460       if ((0 == PL_strcmp(target, "newwindow")) ||
   461           (0 == PL_strcmp(target, "_new")))
   462         target = "_blank";
   463       else if (0 == PL_strcmp(target, "_current"))
   464         target = "_self";
   466       rv = owner->GetURL(url, target, nullptr, nullptr, 0);
   467     }
   468   }
   470   if (streamListener)
   471     rv = NewPluginURLStream(NS_ConvertUTF8toUTF16(url), pluginInst,
   472                             streamListener, nullptr,
   473                             getHeaders, getHeadersLength);
   475   return rv;
   476 }
   478 nsresult nsPluginHost::PostURL(nsISupports* pluginInst,
   479                                     const char* url,
   480                                     uint32_t postDataLen,
   481                                     const char* postData,
   482                                     bool isFile,
   483                                     const char* target,
   484                                     nsNPAPIPluginStreamListener* streamListener,
   485                                     const char* altHost,
   486                                     const char* referrer,
   487                                     bool forceJSEnabled,
   488                                     uint32_t postHeadersLength,
   489                                     const char* postHeaders)
   490 {
   491   nsresult rv;
   493   // we can only send a stream back to the plugin (as specified
   494   // by a null target) if we also have a nsNPAPIPluginStreamListener
   495   // to talk to also
   496   if (!target && !streamListener)
   497     return NS_ERROR_ILLEGAL_VALUE;
   499   nsNPAPIPluginInstance* instance = static_cast<nsNPAPIPluginInstance*>(pluginInst);
   501   rv = DoURLLoadSecurityCheck(instance, url);
   502   if (NS_FAILED(rv))
   503     return rv;
   505   nsCOMPtr<nsIInputStream> postStream;
   506   if (isFile) {
   507     nsCOMPtr<nsIFile> file;
   508     rv = CreateTempFileToPost(postData, getter_AddRefs(file));
   509     if (NS_FAILED(rv))
   510       return rv;
   512     nsCOMPtr<nsIInputStream> fileStream;
   513     rv = NS_NewLocalFileInputStream(getter_AddRefs(fileStream),
   514                                     file,
   515                                     PR_RDONLY,
   516                                     0600,
   517                                     nsIFileInputStream::DELETE_ON_CLOSE |
   518                                     nsIFileInputStream::CLOSE_ON_EOF);
   519     if (NS_FAILED(rv))
   520       return rv;
   522     rv = NS_NewBufferedInputStream(getter_AddRefs(postStream), fileStream, 8192);
   523     if (NS_FAILED(rv))
   524       return rv;
   525   } else {
   526     char *dataToPost;
   527     uint32_t newDataToPostLen;
   528     ParsePostBufferToFixHeaders(postData, postDataLen, &dataToPost, &newDataToPostLen);
   529     if (!dataToPost)
   530       return NS_ERROR_UNEXPECTED;
   532     nsCOMPtr<nsIStringInputStream> sis = do_CreateInstance("@mozilla.org/io/string-input-stream;1", &rv);
   533     if (!sis) {
   534       NS_Free(dataToPost);
   535       return rv;
   536     }
   538     // data allocated by ParsePostBufferToFixHeaders() is managed and
   539     // freed by the string stream.
   540     postDataLen = newDataToPostLen;
   541     sis->AdoptData(dataToPost, postDataLen);
   542     postStream = sis;
   543   }
   545   if (target) {
   546     nsRefPtr<nsPluginInstanceOwner> owner = instance->GetOwner();
   547     if (owner) {
   548       if ((0 == PL_strcmp(target, "newwindow")) ||
   549           (0 == PL_strcmp(target, "_new"))) {
   550         target = "_blank";
   551       } else if (0 == PL_strcmp(target, "_current")) {
   552         target = "_self";
   553       }
   554       rv = owner->GetURL(url, target, postStream,
   555                          (void*)postHeaders, postHeadersLength);
   556     }
   557   }
   559   // if we don't have a target, just create a stream.  This does
   560   // NS_OpenURI()!
   561   if (streamListener)
   562     rv = NewPluginURLStream(NS_ConvertUTF8toUTF16(url), instance,
   563                             streamListener,
   564                             postStream, postHeaders, postHeadersLength);
   566   return rv;
   567 }
   569 /* This method queries the prefs for proxy information.
   570  * It has been tested and is known to work in the following three cases
   571  * when no proxy host or port is specified
   572  * when only the proxy host is specified
   573  * when only the proxy port is specified
   574  * This method conforms to the return code specified in
   575  * http://developer.netscape.com/docs/manuals/proxy/adminnt/autoconf.htm#1020923
   576  * with the exception that multiple values are not implemented.
   577  */
   579 nsresult nsPluginHost::FindProxyForURL(const char* url, char* *result)
   580 {
   581   if (!url || !result) {
   582     return NS_ERROR_INVALID_ARG;
   583   }
   584   nsresult res;
   586   nsCOMPtr<nsIProtocolProxyService> proxyService =
   587     do_GetService(NS_PROTOCOLPROXYSERVICE_CONTRACTID, &res);
   588   if (NS_FAILED(res) || !proxyService)
   589     return res;
   591   nsRefPtr<nsProtocolProxyService> rawProxyService = do_QueryObject(proxyService);
   592   if (!rawProxyService)
   593     return NS_ERROR_FAILURE;
   595   nsCOMPtr<nsIIOService> ioService = do_GetService(NS_IOSERVICE_CONTRACTID, &res);
   596   if (NS_FAILED(res) || !ioService)
   597     return res;
   599   // make a temporary channel from the argument url
   600   nsCOMPtr<nsIChannel> tempChannel;
   601   res = ioService->NewChannel(nsDependentCString(url), nullptr, nullptr, getter_AddRefs(tempChannel));
   602   if (NS_FAILED(res))
   603     return res;
   605   nsCOMPtr<nsIProxyInfo> pi;
   607   // Remove this deprecated call in the future (see Bug 778201):
   608   res = rawProxyService->DeprecatedBlockingResolve(tempChannel, 0, getter_AddRefs(pi));
   609   if (NS_FAILED(res))
   610     return res;
   612   nsAutoCString host, type;
   613   int32_t port = -1;
   615   // These won't fail, and even if they do... we'll be ok.
   616   if (pi) {
   617     pi->GetType(type);
   618     pi->GetHost(host);
   619     pi->GetPort(&port);
   620   }
   622   if (!pi || host.IsEmpty() || port <= 0 || host.EqualsLiteral("direct")) {
   623     *result = PL_strdup("DIRECT");
   624   } else if (type.EqualsLiteral("http")) {
   625     *result = PR_smprintf("PROXY %s:%d", host.get(), port);
   626   } else if (type.EqualsLiteral("socks4")) {
   627     *result = PR_smprintf("SOCKS %s:%d", host.get(), port);
   628   } else if (type.EqualsLiteral("socks")) {
   629     // XXX - this is socks5, but there is no API for us to tell the
   630     // plugin that fact. SOCKS for now, in case the proxy server
   631     // speaks SOCKS4 as well. See bug 78176
   632     // For a long time this was returning an http proxy type, so
   633     // very little is probably broken by this
   634     *result = PR_smprintf("SOCKS %s:%d", host.get(), port);
   635   } else {
   636     NS_ASSERTION(false, "Unknown proxy type!");
   637     *result = PL_strdup("DIRECT");
   638   }
   640   if (nullptr == *result)
   641     res = NS_ERROR_OUT_OF_MEMORY;
   643   return res;
   644 }
   646 nsresult nsPluginHost::Init()
   647 {
   648   return NS_OK;
   649 }
   651 nsresult nsPluginHost::UnloadPlugins()
   652 {
   653   PLUGIN_LOG(PLUGIN_LOG_NORMAL, ("nsPluginHost::UnloadPlugins Called\n"));
   655   if (!mPluginsLoaded)
   656     return NS_OK;
   658   // we should call nsIPluginInstance::Stop and nsIPluginInstance::SetWindow
   659   // for those plugins who want it
   660   DestroyRunningInstances(nullptr);
   662   nsPluginTag *pluginTag;
   663   for (pluginTag = mPlugins; pluginTag; pluginTag = pluginTag->mNext) {
   664     pluginTag->TryUnloadPlugin(true);
   665   }
   667   NS_ITERATIVE_UNREF_LIST(nsRefPtr<nsPluginTag>, mPlugins, mNext);
   668   NS_ITERATIVE_UNREF_LIST(nsRefPtr<nsPluginTag>, mCachedPlugins, mNext);
   669   NS_ITERATIVE_UNREF_LIST(nsRefPtr<nsInvalidPluginTag>, mInvalidPlugins, mNext);
   671   // Lets remove any of the temporary files that we created.
   672   if (sPluginTempDir) {
   673     sPluginTempDir->Remove(true);
   674     NS_RELEASE(sPluginTempDir);
   675   }
   677 #ifdef XP_WIN
   678   if (mPrivateDirServiceProvider) {
   679     nsCOMPtr<nsIDirectoryService> dirService =
   680       do_GetService(kDirectoryServiceContractID);
   681     if (dirService)
   682       dirService->UnregisterProvider(mPrivateDirServiceProvider);
   683     mPrivateDirServiceProvider = nullptr;
   684   }
   685 #endif /* XP_WIN */
   687   mPluginsLoaded = false;
   689   return NS_OK;
   690 }
   692 void nsPluginHost::OnPluginInstanceDestroyed(nsPluginTag* aPluginTag)
   693 {
   694   bool hasInstance = false;
   695   for (uint32_t i = 0; i < mInstances.Length(); i++) {
   696     if (TagForPlugin(mInstances[i]->GetPlugin()) == aPluginTag) {
   697       hasInstance = true;
   698       break;
   699     }
   700   }
   702   // We have some options for unloading plugins if they have no instances.
   703   //
   704   // Unloading plugins immediately can be bad - some plugins retain state
   705   // between instances even when there are none. This is largely limited to
   706   // going from one page to another, so state is retained without an instance
   707   // for only a very short period of time. In order to allow this to work
   708   // we don't unload plugins immediately by default. This is supported
   709   // via a hidden user pref though.
   710   //
   711   // Another reason not to unload immediately is that loading is expensive,
   712   // and it is better to leave popular plugins loaded.
   713   //
   714   // Our default behavior is to try to unload a plugin three minutes after
   715   // its last instance is destroyed. This seems like a reasonable compromise
   716   // that allows us to reclaim memory while allowing short state retention
   717   // and avoid perf hits for loading popular plugins.
   718   if (!hasInstance) {
   719     if (UnloadPluginsASAP()) {
   720       aPluginTag->TryUnloadPlugin(false);
   721     } else {
   722       if (aPluginTag->mUnloadTimer) {
   723         aPluginTag->mUnloadTimer->Cancel();
   724       } else {
   725         aPluginTag->mUnloadTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
   726       }
   727       aPluginTag->mUnloadTimer->InitWithCallback(this, 1000 * 60 * 3, nsITimer::TYPE_ONE_SHOT);
   728     }
   729   }
   730 }
   732 nsresult
   733 nsPluginHost::GetPluginTempDir(nsIFile **aDir)
   734 {
   735   if (!sPluginTempDir) {
   736     nsCOMPtr<nsIFile> tmpDir;
   737     nsresult rv = NS_GetSpecialDirectory(NS_OS_TEMP_DIR,
   738                                          getter_AddRefs(tmpDir));
   739     NS_ENSURE_SUCCESS(rv, rv);
   741     rv = tmpDir->AppendNative(kPluginTmpDirName);
   743     // make it unique, and mode == 0700, not world-readable
   744     rv = tmpDir->CreateUnique(nsIFile::DIRECTORY_TYPE, 0700);
   745     NS_ENSURE_SUCCESS(rv, rv);
   747     tmpDir.swap(sPluginTempDir);
   748   }
   750   return sPluginTempDir->Clone(aDir);
   751 }
   753 nsresult
   754 nsPluginHost::InstantiatePluginInstance(const char *aMimeType, nsIURI* aURL,
   755                                         nsObjectLoadingContent *aContent,
   756                                         nsPluginInstanceOwner** aOwner)
   757 {
   758   NS_ENSURE_ARG_POINTER(aOwner);
   760 #ifdef PLUGIN_LOGGING
   761   nsAutoCString urlSpec;
   762   if (aURL)
   763     aURL->GetAsciiSpec(urlSpec);
   765   PR_LOG(nsPluginLogging::gPluginLog, PLUGIN_LOG_NORMAL,
   766         ("nsPluginHost::InstantiatePlugin Begin mime=%s, url=%s\n",
   767         aMimeType, urlSpec.get()));
   769   PR_LogFlush();
   770 #endif
   772   if (!aMimeType) {
   773     NS_NOTREACHED("Attempting to spawn a plugin with no mime type");
   774     return NS_ERROR_FAILURE;
   775   }
   777   nsRefPtr<nsPluginInstanceOwner> instanceOwner = new nsPluginInstanceOwner();
   778   if (!instanceOwner) {
   779     return NS_ERROR_OUT_OF_MEMORY;
   780   }
   782   nsCOMPtr<nsIContent> ourContent = do_QueryInterface(static_cast<nsIImageLoadingContent*>(aContent));
   783   nsresult rv = instanceOwner->Init(ourContent);
   784   if (NS_FAILED(rv)) {
   785     return rv;
   786   }
   788   nsPluginTagType tagType;
   789   rv = instanceOwner->GetTagType(&tagType);
   790   if (NS_FAILED(rv)) {
   791     return rv;
   792   }
   794   if (tagType != nsPluginTagType_Embed &&
   795       tagType != nsPluginTagType_Applet &&
   796       tagType != nsPluginTagType_Object) {
   797     return NS_ERROR_FAILURE;
   798   }
   800   rv = SetUpPluginInstance(aMimeType, aURL, instanceOwner);
   801   if (NS_FAILED(rv)) {
   802     return NS_ERROR_FAILURE;
   803   }
   805   nsRefPtr<nsNPAPIPluginInstance> instance;
   806   rv = instanceOwner->GetInstance(getter_AddRefs(instance));
   807   if (NS_FAILED(rv)) {
   808     return rv;
   809   }
   811   if (instance) {
   812     instanceOwner->CreateWidget();
   814     // If we've got a native window, the let the plugin know about it.
   815     instanceOwner->CallSetWindow();
   816   }
   818   // At this point we consider instantiation to be successful. Do not return an error.
   819   instanceOwner.forget(aOwner);
   821 #ifdef PLUGIN_LOGGING
   822   nsAutoCString urlSpec2;
   823   if (aURL != nullptr) aURL->GetAsciiSpec(urlSpec2);
   825   PR_LOG(nsPluginLogging::gPluginLog, PLUGIN_LOG_NORMAL,
   826         ("nsPluginHost::InstantiatePlugin Finished mime=%s, rv=%d, url=%s\n",
   827         aMimeType, rv, urlSpec2.get()));
   829   PR_LogFlush();
   830 #endif
   832   return NS_OK;
   833 }
   835 nsPluginTag*
   836 nsPluginHost::FindTagForLibrary(PRLibrary* aLibrary)
   837 {
   838   nsPluginTag* pluginTag;
   839   for (pluginTag = mPlugins; pluginTag; pluginTag = pluginTag->mNext) {
   840     if (pluginTag->mLibrary == aLibrary) {
   841       return pluginTag;
   842     }
   843   }
   844   return nullptr;
   845 }
   847 nsPluginTag*
   848 nsPluginHost::TagForPlugin(nsNPAPIPlugin* aPlugin)
   849 {
   850   nsPluginTag* pluginTag;
   851   for (pluginTag = mPlugins; pluginTag; pluginTag = pluginTag->mNext) {
   852     if (pluginTag->mPlugin == aPlugin) {
   853       return pluginTag;
   854     }
   855   }
   856   // a plugin should never exist without a corresponding tag
   857   NS_ERROR("TagForPlugin has failed");
   858   return nullptr;
   859 }
   861 nsresult nsPluginHost::SetUpPluginInstance(const char *aMimeType,
   862                                            nsIURI *aURL,
   863                                            nsPluginInstanceOwner *aOwner)
   864 {
   865   NS_ENSURE_ARG_POINTER(aOwner);
   867   nsresult rv = TrySetUpPluginInstance(aMimeType, aURL, aOwner);
   868   if (NS_SUCCEEDED(rv)) {
   869     return rv;
   870   }
   872   // If we failed to load a plugin instance we'll try again after
   873   // reloading our plugin list. Only do that once per document to
   874   // avoid redundant high resource usage on pages with multiple
   875   // unkown instance types. We'll do that by caching the document.
   876   nsCOMPtr<nsIDocument> document;
   877   aOwner->GetDocument(getter_AddRefs(document));
   879   nsCOMPtr<nsIDocument> currentdocument = do_QueryReferent(mCurrentDocument);
   880   if (document == currentdocument) {
   881     return rv;
   882   }
   884   mCurrentDocument = do_GetWeakReference(document);
   886   // Don't try to set up an instance again if nothing changed.
   887   if (ReloadPlugins() == NS_ERROR_PLUGINS_PLUGINSNOTCHANGED) {
   888     return rv;
   889   }
   891   return TrySetUpPluginInstance(aMimeType, aURL, aOwner);
   892 }
   894 nsresult
   895 nsPluginHost::TrySetUpPluginInstance(const char *aMimeType,
   896                                      nsIURI *aURL,
   897                                      nsPluginInstanceOwner *aOwner)
   898 {
   899 #ifdef PLUGIN_LOGGING
   900   nsAutoCString urlSpec;
   901   if (aURL != nullptr) aURL->GetSpec(urlSpec);
   903   PR_LOG(nsPluginLogging::gPluginLog, PLUGIN_LOG_NORMAL,
   904         ("nsPluginHost::TrySetupPluginInstance Begin mime=%s, owner=%p, url=%s\n",
   905         aMimeType, aOwner, urlSpec.get()));
   907   PR_LogFlush();
   908 #endif
   910   nsRefPtr<nsNPAPIPlugin> plugin;
   911   GetPlugin(aMimeType, getter_AddRefs(plugin));
   912   if (!plugin) {
   913     return NS_ERROR_FAILURE;
   914   }
   916   nsPluginTag* pluginTag = FindPluginForType(aMimeType, true);
   918   NS_ASSERTION(pluginTag, "Must have plugin tag here!");
   920 #if defined(MOZ_WIDGET_ANDROID) && defined(MOZ_CRASHREPORTER)
   921   if (pluginTag->mIsFlashPlugin) {
   922     CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("FlashVersion"), pluginTag->mVersion);
   923   }
   924 #endif
   926   nsRefPtr<nsNPAPIPluginInstance> instance = new nsNPAPIPluginInstance();
   928   // This will create the owning reference. The connection must be made between the
   929   // instance and the instance owner before initialization. Plugins can call into
   930   // the browser during initialization.
   931   aOwner->SetInstance(instance.get());
   933   // Add the instance to the instances list before we call NPP_New so that
   934   // it is "in play" before NPP_New happens. Take it out if NPP_New fails.
   935   mInstances.AppendElement(instance.get());
   937   // this should not addref the instance or owner
   938   // except in some cases not Java, see bug 140931
   939   // our COM pointer will free the peer
   940   nsresult rv = instance->Initialize(plugin.get(), aOwner, aMimeType);
   941   if (NS_FAILED(rv)) {
   942     mInstances.RemoveElement(instance.get());
   943     aOwner->SetInstance(nullptr);
   944     return rv;
   945   }
   947   // Cancel the plugin unload timer since we are creating
   948   // an instance for it.
   949   if (pluginTag->mUnloadTimer) {
   950     pluginTag->mUnloadTimer->Cancel();
   951   }
   953 #ifdef PLUGIN_LOGGING
   954   nsAutoCString urlSpec2;
   955   if (aURL)
   956     aURL->GetSpec(urlSpec2);
   958   PR_LOG(nsPluginLogging::gPluginLog, PLUGIN_LOG_BASIC,
   959         ("nsPluginHost::TrySetupPluginInstance Finished mime=%s, rv=%d, owner=%p, url=%s\n",
   960         aMimeType, rv, aOwner, urlSpec2.get()));
   962   PR_LogFlush();
   963 #endif
   965   return rv;
   966 }
   968 bool
   969 nsPluginHost::PluginExistsForType(const char* aMimeType)
   970 {
   971   nsPluginTag *plugin = FindPluginForType(aMimeType, false);
   972   return nullptr != plugin;
   973 }
   975 NS_IMETHODIMP
   976 nsPluginHost::GetPluginTagForType(const nsACString& aMimeType,
   977                                   nsIPluginTag** aResult)
   978 {
   979   nsPluginTag* plugin = FindPluginForType(aMimeType.Data(), true);
   980   if (!plugin) {
   981     plugin = FindPluginForType(aMimeType.Data(), false);
   982   }
   983   if (!plugin) {
   984     return NS_ERROR_NOT_AVAILABLE;
   985   }
   986   NS_ADDREF(*aResult = plugin);
   987   return NS_OK;
   988 }
   990 NS_IMETHODIMP
   991 nsPluginHost::GetStateForType(const nsACString &aMimeType, uint32_t* aResult)
   992 {
   993   nsPluginTag *plugin = FindPluginForType(aMimeType.Data(), true);
   994   if (!plugin) {
   995     plugin = FindPluginForType(aMimeType.Data(), false);
   996   }
   997   if (!plugin) {
   998     return NS_ERROR_UNEXPECTED;
   999   }
  1001   return plugin->GetEnabledState(aResult);
  1004 NS_IMETHODIMP
  1005 nsPluginHost::GetBlocklistStateForType(const char *aMimeType, uint32_t *aState)
  1007   nsPluginTag *plugin = FindPluginForType(aMimeType, true);
  1008   if (!plugin) {
  1009     plugin = FindPluginForType(aMimeType, false);
  1011   if (!plugin) {
  1012     return NS_ERROR_FAILURE;
  1015   *aState = plugin->GetBlocklistState();
  1016   return NS_OK;
  1019 NS_IMETHODIMP
  1020 nsPluginHost::GetPermissionStringForType(const nsACString &aMimeType, nsACString &aPermissionString)
  1022   aPermissionString.Truncate();
  1023   uint32_t blocklistState;
  1024   nsresult rv = GetBlocklistStateForType(aMimeType.Data(), &blocklistState);
  1025   NS_ENSURE_SUCCESS(rv, rv);
  1026   nsPluginTag *tag = FindPluginForType(aMimeType.Data(), true);
  1027   if (!tag) {
  1028     tag = FindPluginForType(aMimeType.Data(), false);
  1030   if (!tag) {
  1031     return NS_ERROR_FAILURE;
  1034   if (blocklistState == nsIBlocklistService::STATE_VULNERABLE_UPDATE_AVAILABLE ||
  1035       blocklistState == nsIBlocklistService::STATE_VULNERABLE_NO_UPDATE) {
  1036     aPermissionString.AssignLiteral("plugin-vulnerable:");
  1038   else {
  1039     aPermissionString.AssignLiteral("plugin:");
  1042   aPermissionString.Append(tag->GetNiceFileName());
  1044   return NS_OK;
  1047 // check comma delimitered extensions
  1048 static int CompareExtensions(const char *aExtensionList, const char *aExtension)
  1050   if (!aExtensionList || !aExtension)
  1051     return -1;
  1053   const char *pExt = aExtensionList;
  1054   const char *pComma = strchr(pExt, ',');
  1055   if (!pComma)
  1056     return PL_strcasecmp(pExt, aExtension);
  1058   int extlen = strlen(aExtension);
  1059   while (pComma) {
  1060     int length = pComma - pExt;
  1061     if (length == extlen && 0 == PL_strncasecmp(aExtension, pExt, length))
  1062       return 0;
  1063     pComma++;
  1064     pExt = pComma;
  1065     pComma = strchr(pExt, ',');
  1068   // the last one
  1069   return PL_strcasecmp(pExt, aExtension);
  1072 nsresult
  1073 nsPluginHost::IsPluginEnabledForExtension(const char* aExtension,
  1074                                           const char* &aMimeType)
  1076   nsPluginTag *plugin = FindPluginEnabledForExtension(aExtension, aMimeType);
  1077   if (plugin)
  1078     return NS_OK;
  1080   return NS_ERROR_FAILURE;
  1083 void
  1084 nsPluginHost::GetPlugins(nsTArray<nsRefPtr<nsPluginTag> >& aPluginArray)
  1086   aPluginArray.Clear();
  1088   LoadPlugins();
  1090   nsPluginTag* plugin = mPlugins;
  1091   while (plugin != nullptr) {
  1092     if (plugin->IsEnabled()) {
  1093       aPluginArray.AppendElement(plugin);
  1095     plugin = plugin->mNext;
  1099 NS_IMETHODIMP
  1100 nsPluginHost::GetPluginTags(uint32_t* aPluginCount, nsIPluginTag*** aResults)
  1102   LoadPlugins();
  1104   uint32_t count = 0;
  1105   nsRefPtr<nsPluginTag> plugin = mPlugins;
  1106   while (plugin != nullptr) {
  1107     count++;
  1108     plugin = plugin->mNext;
  1111   *aResults = static_cast<nsIPluginTag**>
  1112                          (nsMemory::Alloc(count * sizeof(**aResults)));
  1113   if (!*aResults)
  1114     return NS_ERROR_OUT_OF_MEMORY;
  1116   *aPluginCount = count;
  1118   plugin = mPlugins;
  1119   for (uint32_t i = 0; i < count; i++) {
  1120     (*aResults)[i] = plugin;
  1121     NS_ADDREF((*aResults)[i]);
  1122     plugin = plugin->mNext;
  1125   return NS_OK;
  1128 nsPluginTag*
  1129 nsPluginHost::FindPreferredPlugin(const InfallibleTArray<nsPluginTag*>& matches)
  1131   // We prefer the plugin with the highest version number.
  1132   /// XXX(johns): This seems to assume the only time multiple plugins will have
  1133   ///             the same MIME type is if they're multiple versions of the same
  1134   ///             plugin -- but since plugin filenames and pretty names can both
  1135   ///             update, it's probably less arbitrary than just going at it
  1136   ///             alphabetically.
  1138   if (matches.IsEmpty()) {
  1139     return nullptr;
  1142   nsPluginTag *preferredPlugin = matches[0];
  1143   for (unsigned int i = 1; i < matches.Length(); i++) {
  1144     if (mozilla::Version(matches[i]->mVersion.get()) > preferredPlugin->mVersion.get()) {
  1145       preferredPlugin = matches[i];
  1149   return preferredPlugin;
  1152 nsPluginTag*
  1153 nsPluginHost::FindPluginForType(const char* aMimeType,
  1154                                 bool aCheckEnabled)
  1156   if (!aMimeType) {
  1157     return nullptr;
  1160   LoadPlugins();
  1162   InfallibleTArray<nsPluginTag*> matchingPlugins;
  1164   nsPluginTag *plugin = mPlugins;
  1165   while (plugin) {
  1166     if (!aCheckEnabled || plugin->IsActive()) {
  1167       int32_t mimeCount = plugin->mMimeTypes.Length();
  1168       for (int32_t i = 0; i < mimeCount; i++) {
  1169         if (0 == PL_strcasecmp(plugin->mMimeTypes[i].get(), aMimeType)) {
  1170           matchingPlugins.AppendElement(plugin);
  1171           break;
  1175     plugin = plugin->mNext;
  1178   return FindPreferredPlugin(matchingPlugins);
  1181 nsPluginTag*
  1182 nsPluginHost::FindPluginEnabledForExtension(const char* aExtension,
  1183                                             const char*& aMimeType)
  1185   if (!aExtension) {
  1186     return nullptr;
  1189   LoadPlugins();
  1191   InfallibleTArray<nsPluginTag*> matchingPlugins;
  1193   nsPluginTag *plugin = mPlugins;
  1194   while (plugin) {
  1195     if (plugin->IsActive()) {
  1196       int32_t variants = plugin->mExtensions.Length();
  1197       for (int32_t i = 0; i < variants; i++) {
  1198         // mExtensionsArray[cnt] is a list of extensions separated by commas
  1199         if (0 == CompareExtensions(plugin->mExtensions[i].get(), aExtension)) {
  1200           matchingPlugins.AppendElement(plugin);
  1201           break;
  1205     plugin = plugin->mNext;
  1208   nsPluginTag *preferredPlugin = FindPreferredPlugin(matchingPlugins);
  1209   if (!preferredPlugin) {
  1210     return nullptr;
  1213   int32_t variants = preferredPlugin->mExtensions.Length();
  1214   for (int32_t i = 0; i < variants; i++) {
  1215     // mExtensionsArray[cnt] is a list of extensions separated by commas
  1216     if (0 == CompareExtensions(preferredPlugin->mExtensions[i].get(), aExtension)) {
  1217       aMimeType = preferredPlugin->mMimeTypes[i].get();
  1218       break;
  1222   return preferredPlugin;
  1225 static nsresult CreateNPAPIPlugin(nsPluginTag *aPluginTag,
  1226                                   nsNPAPIPlugin **aOutNPAPIPlugin)
  1228   // If this is an in-process plugin we'll need to load it here if we haven't already.
  1229   if (!nsNPAPIPlugin::RunPluginOOP(aPluginTag)) {
  1230     if (aPluginTag->mFullPath.IsEmpty())
  1231       return NS_ERROR_FAILURE;
  1232     nsCOMPtr<nsIFile> file = do_CreateInstance("@mozilla.org/file/local;1");
  1233     file->InitWithPath(NS_ConvertUTF8toUTF16(aPluginTag->mFullPath));
  1234     nsPluginFile pluginFile(file);
  1235     PRLibrary* pluginLibrary = nullptr;
  1237     if (NS_FAILED(pluginFile.LoadPlugin(&pluginLibrary)) || !pluginLibrary)
  1238       return NS_ERROR_FAILURE;
  1240     aPluginTag->mLibrary = pluginLibrary;
  1243   nsresult rv;
  1244   rv = nsNPAPIPlugin::CreatePlugin(aPluginTag, aOutNPAPIPlugin);
  1246   return rv;
  1249 nsresult nsPluginHost::EnsurePluginLoaded(nsPluginTag* aPluginTag)
  1251   nsRefPtr<nsNPAPIPlugin> plugin = aPluginTag->mPlugin;
  1252   if (!plugin) {
  1253     nsresult rv = CreateNPAPIPlugin(aPluginTag, getter_AddRefs(plugin));
  1254     if (NS_FAILED(rv)) {
  1255       return rv;
  1257     aPluginTag->mPlugin = plugin;
  1259   return NS_OK;
  1262 nsresult nsPluginHost::GetPlugin(const char *aMimeType, nsNPAPIPlugin** aPlugin)
  1264   nsresult rv = NS_ERROR_FAILURE;
  1265   *aPlugin = nullptr;
  1267   if (!aMimeType)
  1268     return NS_ERROR_ILLEGAL_VALUE;
  1270   // If plugins haven't been scanned yet, do so now
  1271   LoadPlugins();
  1273   nsPluginTag* pluginTag = FindPluginForType(aMimeType, true);
  1274   if (pluginTag) {
  1275     rv = NS_OK;
  1276     PLUGIN_LOG(PLUGIN_LOG_BASIC,
  1277     ("nsPluginHost::GetPlugin Begin mime=%s, plugin=%s\n",
  1278     aMimeType, pluginTag->mFileName.get()));
  1280 #ifdef DEBUG
  1281     if (aMimeType && !pluginTag->mFileName.IsEmpty())
  1282       printf("For %s found plugin %s\n", aMimeType, pluginTag->mFileName.get());
  1283 #endif
  1285     rv = EnsurePluginLoaded(pluginTag);
  1286     if (NS_FAILED(rv)) {
  1287       return rv;
  1290     NS_ADDREF(*aPlugin = pluginTag->mPlugin);
  1291     return NS_OK;
  1294   PLUGIN_LOG(PLUGIN_LOG_NORMAL,
  1295   ("nsPluginHost::GetPlugin End mime=%s, rv=%d, plugin=%p name=%s\n",
  1296   aMimeType, rv, *aPlugin,
  1297   (pluginTag ? pluginTag->mFileName.get() : "(not found)")));
  1299   return rv;
  1302 // Normalize 'host' to ACE.
  1303 nsresult
  1304 nsPluginHost::NormalizeHostname(nsCString& host)
  1306   if (IsASCII(host)) {
  1307     ToLowerCase(host);
  1308     return NS_OK;
  1311   if (!mIDNService) {
  1312     nsresult rv;
  1313     mIDNService = do_GetService(NS_IDNSERVICE_CONTRACTID, &rv);
  1314     NS_ENSURE_SUCCESS(rv, rv);
  1317   return mIDNService->ConvertUTF8toACE(host, host);
  1320 // Enumerate a 'sites' array returned by GetSitesWithData and determine if
  1321 // any of them have a base domain in common with 'domain'; if so, append them
  1322 // to the 'result' array. If 'firstMatchOnly' is true, return after finding the
  1323 // first match.
  1324 nsresult
  1325 nsPluginHost::EnumerateSiteData(const nsACString& domain,
  1326                                 const InfallibleTArray<nsCString>& sites,
  1327                                 InfallibleTArray<nsCString>& result,
  1328                                 bool firstMatchOnly)
  1330   NS_ASSERTION(!domain.IsVoid(), "null domain string");
  1332   nsresult rv;
  1333   if (!mTLDService) {
  1334     mTLDService = do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID, &rv);
  1335     NS_ENSURE_SUCCESS(rv, rv);
  1338   // Get the base domain from the domain.
  1339   nsCString baseDomain;
  1340   rv = mTLDService->GetBaseDomainFromHost(domain, 0, baseDomain);
  1341   bool isIP = rv == NS_ERROR_HOST_IS_IP_ADDRESS;
  1342   if (isIP || rv == NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS) {
  1343     // The base domain is the site itself. However, we must be careful to
  1344     // normalize.
  1345     baseDomain = domain;
  1346     rv = NormalizeHostname(baseDomain);
  1347     NS_ENSURE_SUCCESS(rv, rv);
  1348   } else if (NS_FAILED(rv)) {
  1349     return rv;
  1352   // Enumerate the array of sites with data.
  1353   for (uint32_t i = 0; i < sites.Length(); ++i) {
  1354     const nsCString& site = sites[i];
  1356     // Check if the site is an IP address.
  1357     bool siteIsIP =
  1358       site.Length() >= 2 && site.First() == '[' && site.Last() == ']';
  1359     if (siteIsIP != isIP)
  1360       continue;
  1362     nsCString siteBaseDomain;
  1363     if (siteIsIP) {
  1364       // Strip the '[]'.
  1365       siteBaseDomain = Substring(site, 1, site.Length() - 2);
  1366     } else {
  1367       // Determine the base domain of the site.
  1368       rv = mTLDService->GetBaseDomainFromHost(site, 0, siteBaseDomain);
  1369       if (rv == NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS) {
  1370         // The base domain is the site itself. However, we must be careful to
  1371         // normalize.
  1372         siteBaseDomain = site;
  1373         rv = NormalizeHostname(siteBaseDomain);
  1374         NS_ENSURE_SUCCESS(rv, rv);
  1375       } else if (NS_FAILED(rv)) {
  1376         return rv;
  1380     // At this point, we can do an exact comparison of the two domains.
  1381     if (baseDomain != siteBaseDomain) {
  1382       continue;
  1385     // Append the site to the result array.
  1386     result.AppendElement(site);
  1388     // If we're supposed to return early, do so.
  1389     if (firstMatchOnly) {
  1390       break;
  1394   return NS_OK;
  1397 NS_IMETHODIMP
  1398 nsPluginHost::RegisterPlayPreviewMimeType(const nsACString& mimeType,
  1399                                           bool ignoreCTP,
  1400                                           const nsACString& redirectURL)
  1402   nsAutoCString mt(mimeType);
  1403   nsAutoCString url(redirectURL);
  1404   if (url.Length() == 0) {
  1405     // using default play preview iframe URL, if redirectURL is not specified
  1406     url.Assign("data:application/x-moz-playpreview;,");
  1407     url.Append(mimeType);
  1410   nsRefPtr<nsPluginPlayPreviewInfo> playPreview =
  1411     new nsPluginPlayPreviewInfo(mt.get(), ignoreCTP, url.get());
  1412   mPlayPreviewMimeTypes.AppendElement(playPreview);
  1413   return NS_OK;
  1416 NS_IMETHODIMP
  1417 nsPluginHost::UnregisterPlayPreviewMimeType(const nsACString& mimeType)
  1419   nsAutoCString mimeTypeToRemove(mimeType);
  1420   for (uint32_t i = mPlayPreviewMimeTypes.Length(); i > 0; i--) {
  1421     nsRefPtr<nsPluginPlayPreviewInfo> pp = mPlayPreviewMimeTypes[i - 1];
  1422     if (PL_strcasecmp(pp.get()->mMimeType.get(), mimeTypeToRemove.get()) == 0) {
  1423       mPlayPreviewMimeTypes.RemoveElementAt(i - 1);
  1424       break;
  1427   return NS_OK;
  1430 NS_IMETHODIMP
  1431 nsPluginHost::GetPlayPreviewInfo(const nsACString& mimeType,
  1432                                  nsIPluginPlayPreviewInfo** aResult)
  1434   nsAutoCString mimeTypeToFind(mimeType);
  1435   for (uint32_t i = 0; i < mPlayPreviewMimeTypes.Length(); i++) {
  1436     nsRefPtr<nsPluginPlayPreviewInfo> pp = mPlayPreviewMimeTypes[i];
  1437     if (PL_strcasecmp(pp.get()->mMimeType.get(), mimeTypeToFind.get()) == 0) {
  1438       *aResult = new nsPluginPlayPreviewInfo(pp.get());
  1439       NS_ADDREF(*aResult);
  1440       return NS_OK;
  1443   *aResult = nullptr;
  1444   return NS_ERROR_NOT_AVAILABLE;
  1447 NS_IMETHODIMP
  1448 nsPluginHost::ClearSiteData(nsIPluginTag* plugin, const nsACString& domain,
  1449                             uint64_t flags, int64_t maxAge)
  1451   // maxAge must be either a nonnegative integer or -1.
  1452   NS_ENSURE_ARG(maxAge >= 0 || maxAge == -1);
  1454   // Caller may give us a tag object that is no longer live.
  1455   if (!IsLiveTag(plugin)) {
  1456     return NS_ERROR_NOT_AVAILABLE;
  1459   nsPluginTag* tag = static_cast<nsPluginTag*>(plugin);
  1461   // We only ensure support for clearing Flash site data for now.
  1462   // We will also attempt to clear data for any plugin that happens
  1463   // to be loaded already.
  1464   if (!tag->mIsFlashPlugin && !tag->mPlugin) {
  1465     return NS_ERROR_FAILURE;
  1468   // Make sure the plugin is loaded.
  1469   nsresult rv = EnsurePluginLoaded(tag);
  1470   if (NS_FAILED(rv)) {
  1471     return rv;
  1474   PluginLibrary* library = tag->mPlugin->GetLibrary();
  1476   // If 'domain' is the null string, clear everything.
  1477   if (domain.IsVoid()) {
  1478     return library->NPP_ClearSiteData(nullptr, flags, maxAge);
  1481   // Get the list of sites from the plugin.
  1482   InfallibleTArray<nsCString> sites;
  1483   rv = library->NPP_GetSitesWithData(sites);
  1484   NS_ENSURE_SUCCESS(rv, rv);
  1486   // Enumerate the sites and build a list of matches.
  1487   InfallibleTArray<nsCString> matches;
  1488   rv = EnumerateSiteData(domain, sites, matches, false);
  1489   NS_ENSURE_SUCCESS(rv, rv);
  1491   // Clear the matches.
  1492   for (uint32_t i = 0; i < matches.Length(); ++i) {
  1493     const nsCString& match = matches[i];
  1494     rv = library->NPP_ClearSiteData(match.get(), flags, maxAge);
  1495     NS_ENSURE_SUCCESS(rv, rv);
  1498   return NS_OK;
  1501 NS_IMETHODIMP
  1502 nsPluginHost::SiteHasData(nsIPluginTag* plugin, const nsACString& domain,
  1503                           bool* result)
  1505   // Caller may give us a tag object that is no longer live.
  1506   if (!IsLiveTag(plugin)) {
  1507     return NS_ERROR_NOT_AVAILABLE;
  1510   nsPluginTag* tag = static_cast<nsPluginTag*>(plugin);
  1512   // We only ensure support for clearing Flash site data for now.
  1513   // We will also attempt to clear data for any plugin that happens
  1514   // to be loaded already.
  1515   if (!tag->mIsFlashPlugin && !tag->mPlugin) {
  1516     return NS_ERROR_FAILURE;
  1519   // Make sure the plugin is loaded.
  1520   nsresult rv = EnsurePluginLoaded(tag);
  1521   if (NS_FAILED(rv)) {
  1522     return rv;
  1525   PluginLibrary* library = tag->mPlugin->GetLibrary();
  1527   // Get the list of sites from the plugin.
  1528   InfallibleTArray<nsCString> sites;
  1529   rv = library->NPP_GetSitesWithData(sites);
  1530   NS_ENSURE_SUCCESS(rv, rv);
  1532   // If there's no data, we're done.
  1533   if (sites.IsEmpty()) {
  1534     *result = false;
  1535     return NS_OK;
  1538   // If 'domain' is the null string, and there's data for at least one site,
  1539   // we're done.
  1540   if (domain.IsVoid()) {
  1541     *result = true;
  1542     return NS_OK;
  1545   // Enumerate the sites and determine if there's a match.
  1546   InfallibleTArray<nsCString> matches;
  1547   rv = EnumerateSiteData(domain, sites, matches, true);
  1548   NS_ENSURE_SUCCESS(rv, rv);
  1550   *result = !matches.IsEmpty();
  1551   return NS_OK;
  1554 bool nsPluginHost::IsJavaMIMEType(const char* aType)
  1556   // The java mime pref may well not be one of these,
  1557   // e.g. application/x-java-test used in the test suite
  1558   nsAdoptingCString javaMIME = Preferences::GetCString(kPrefJavaMIME);
  1559   return aType &&
  1560     (javaMIME.EqualsIgnoreCase(aType) ||
  1561      (0 == PL_strncasecmp(aType, "application/x-java-vm",
  1562                           sizeof("application/x-java-vm") - 1)) ||
  1563      (0 == PL_strncasecmp(aType, "application/x-java-applet",
  1564                           sizeof("application/x-java-applet") - 1)) ||
  1565      (0 == PL_strncasecmp(aType, "application/x-java-bean",
  1566                           sizeof("application/x-java-bean") - 1)));
  1569 // Check whether or not a tag is a live, valid tag, and that it's loaded.
  1570 bool
  1571 nsPluginHost::IsLiveTag(nsIPluginTag* aPluginTag)
  1573   nsPluginTag* tag;
  1574   for (tag = mPlugins; tag; tag = tag->mNext) {
  1575     if (tag == aPluginTag) {
  1576       return true;
  1579   return false;
  1582 nsPluginTag*
  1583 nsPluginHost::HaveSamePlugin(const nsPluginTag* aPluginTag)
  1585   for (nsPluginTag* tag = mPlugins; tag; tag = tag->mNext) {
  1586     if (tag->HasSameNameAndMimes(aPluginTag)) {
  1587         return tag;
  1590   return nullptr;
  1593 nsPluginTag*
  1594 nsPluginHost::FirstPluginWithPath(const nsCString& path)
  1596   for (nsPluginTag* tag = mPlugins; tag; tag = tag->mNext) {
  1597     if (tag->mFullPath.Equals(path)) {
  1598       return tag;
  1601   return nullptr;
  1604 namespace {
  1606 int64_t GetPluginLastModifiedTime(const nsCOMPtr<nsIFile>& localfile)
  1608   PRTime fileModTime = 0;
  1610 #if defined(XP_MACOSX)
  1611   // On OS X the date of a bundle's "contents" (i.e. of its Info.plist file)
  1612   // is a much better guide to when it was last modified than the date of
  1613   // its package directory.  See bug 313700.
  1614   nsCOMPtr<nsILocalFileMac> localFileMac = do_QueryInterface(localfile);
  1615   if (localFileMac) {
  1616     localFileMac->GetBundleContentsLastModifiedTime(&fileModTime);
  1617   } else {
  1618     localfile->GetLastModifiedTime(&fileModTime);
  1620 #else
  1621   localfile->GetLastModifiedTime(&fileModTime);
  1622 #endif
  1624   return fileModTime;
  1627 bool
  1628 GetPluginIsFromExtension(const nsCOMPtr<nsIFile>& pluginFile,
  1629                          const nsCOMArray<nsIFile>& extensionDirs)
  1631   for (uint32_t i = 0; i < extensionDirs.Length(); ++i) {
  1632     bool contains;
  1633     if (NS_FAILED(extensionDirs[i]->Contains(pluginFile, true, &contains)) || !contains) {
  1634       continue;
  1637     return true;
  1640   return false;
  1643 void
  1644 GetExtensionDirectories(nsCOMArray<nsIFile>& dirs)
  1646   nsCOMPtr<nsIProperties> dirService = do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID);
  1647   if (!dirService) {
  1648     return;
  1651   nsCOMPtr<nsISimpleEnumerator> list;
  1652   nsresult rv = dirService->Get(XRE_EXTENSIONS_DIR_LIST,
  1653                                 NS_GET_IID(nsISimpleEnumerator),
  1654                                 getter_AddRefs(list));
  1655   if (NS_FAILED(rv)) {
  1656     return;
  1659   bool more;
  1660   while (NS_SUCCEEDED(list->HasMoreElements(&more)) && more) {
  1661     nsCOMPtr<nsISupports> next;
  1662     if (NS_FAILED(list->GetNext(getter_AddRefs(next)))) {
  1663       break;
  1665     nsCOMPtr<nsIFile> file = do_QueryInterface(next);
  1666     if (file) {
  1667       file->Normalize();
  1668       dirs.AppendElement(file);
  1673 struct CompareFilesByTime
  1675   bool
  1676   LessThan(const nsCOMPtr<nsIFile>& a, const nsCOMPtr<nsIFile>& b) const
  1678     return GetPluginLastModifiedTime(a) < GetPluginLastModifiedTime(b);
  1681   bool
  1682   Equals(const nsCOMPtr<nsIFile>& a, const nsCOMPtr<nsIFile>& b) const
  1684     return GetPluginLastModifiedTime(a) == GetPluginLastModifiedTime(b);
  1686 };
  1688 } // anonymous namespace
  1690 PRBool nsPluginHost::GhettoBlacklist(nsIFile *pluginFile)
  1692     nsCString leaf;
  1693     const char *leafStr;
  1694     nsresult rv;
  1696     rv = pluginFile->GetNativeLeafName(leaf);
  1697     if (NS_FAILED(rv)) {
  1698         return PR_TRUE; // fuck 'em. blacklist.
  1701     leafStr = leaf.get();
  1703     if (!leafStr) {
  1704         return PR_TRUE; // fuck 'em. blacklist.
  1707     // libgnashplugin.so, libflashplayer.so, Flash Player-10.4-10.5.plugin,
  1708     // NPSWF32.dll, NPSWF64.dll
  1709     if (strstr(leafStr, "libgnashplugin") == leafStr ||
  1710         strstr(leafStr, "libflashplayer") == leafStr ||
  1711         strstr(leafStr, "Flash Player") == leafStr ||
  1712         strstr(leafStr, "NPSWF") == leafStr) {
  1713         return PR_FALSE;
  1716     return PR_TRUE; // fuck 'em. blacklist.
  1719 typedef NS_NPAPIPLUGIN_CALLBACK(char *, NP_GETMIMEDESCRIPTION)(void);
  1721 nsresult nsPluginHost::ScanPluginsDirectory(nsIFile *pluginsDir,
  1722                                             bool aCreatePluginList,
  1723                                             bool *aPluginsChanged)
  1725   NS_ENSURE_ARG_POINTER(aPluginsChanged);
  1726   nsresult rv;
  1728   *aPluginsChanged = false;
  1730 #ifdef PLUGIN_LOGGING
  1731   nsAutoCString dirPath;
  1732   pluginsDir->GetNativePath(dirPath);
  1733   PLUGIN_LOG(PLUGIN_LOG_BASIC,
  1734   ("nsPluginHost::ScanPluginsDirectory dir=%s\n", dirPath.get()));
  1735 #endif
  1737   nsCOMPtr<nsISimpleEnumerator> iter;
  1738   rv = pluginsDir->GetDirectoryEntries(getter_AddRefs(iter));
  1739   if (NS_FAILED(rv))
  1740     return rv;
  1742   nsAutoTArray<nsCOMPtr<nsIFile>, 6> pluginFiles;
  1744   bool hasMore;
  1745   while (NS_SUCCEEDED(iter->HasMoreElements(&hasMore)) && hasMore) {
  1746     nsCOMPtr<nsISupports> supports;
  1747     rv = iter->GetNext(getter_AddRefs(supports));
  1748     if (NS_FAILED(rv))
  1749       continue;
  1750     nsCOMPtr<nsIFile> dirEntry(do_QueryInterface(supports, &rv));
  1751     if (NS_FAILED(rv))
  1752       continue;
  1754     // Sun's JRE 1.3.1 plugin must have symbolic links resolved or else it'll crash.
  1755     // See bug 197855.
  1756     dirEntry->Normalize();
  1758     if (nsPluginsDir::IsPluginFile(dirEntry)) {
  1759       pluginFiles.AppendElement(dirEntry);
  1763   pluginFiles.Sort(CompareFilesByTime());
  1765   nsCOMArray<nsIFile> extensionDirs;
  1766   GetExtensionDirectories(extensionDirs);
  1768   bool warnOutdated = false;
  1770   for (int32_t i = (pluginFiles.Length() - 1); i >= 0; i--) {
  1771     nsCOMPtr<nsIFile>& localfile = pluginFiles[i];
  1773     nsString utf16FilePath;
  1774     rv = localfile->GetPath(utf16FilePath);
  1775     if (NS_FAILED(rv))
  1776       continue;
  1778     const int64_t fileModTime = GetPluginLastModifiedTime(localfile);
  1779     const bool fromExtension = GetPluginIsFromExtension(localfile, extensionDirs);
  1781     // Look for it in our cache
  1782     NS_ConvertUTF16toUTF8 filePath(utf16FilePath);
  1783     nsRefPtr<nsPluginTag> pluginTag;
  1784     RemoveCachedPluginsInfo(filePath.get(), getter_AddRefs(pluginTag));
  1786     bool seenBefore = false;
  1788     if (pluginTag) {
  1789       seenBefore = true;
  1790       // If plugin changed, delete cachedPluginTag and don't use cache
  1791       if (fileModTime != pluginTag->mLastModifiedTime) {
  1792         // Plugins has changed. Don't use cached plugin info.
  1793         pluginTag = nullptr;
  1795         // plugin file changed, flag this fact
  1796         *aPluginsChanged = true;
  1799       // If we're not creating a list and we already know something changed then
  1800       // we're done.
  1801       if (!aCreatePluginList) {
  1802         if (*aPluginsChanged) {
  1803           return NS_OK;
  1805         continue;
  1809     bool isKnownInvalidPlugin = false;
  1810     for (nsRefPtr<nsInvalidPluginTag> invalidPlugins = mInvalidPlugins;
  1811          invalidPlugins; invalidPlugins = invalidPlugins->mNext) {
  1812       // If already marked as invalid, ignore it
  1813       if (invalidPlugins->mFullPath.Equals(filePath.get()) &&
  1814           invalidPlugins->mLastModifiedTime == fileModTime) {
  1815         if (aCreatePluginList) {
  1816           invalidPlugins->mSeen = true;
  1818         isKnownInvalidPlugin = true;
  1819         break;
  1822     if (isKnownInvalidPlugin) {
  1823       continue;
  1826     if (GhettoBlacklist(localfile)) {
  1827         continue;
  1830     // if it is not found in cache info list or has been changed, create a new one
  1831     if (!pluginTag) {
  1832       nsPluginFile pluginFile(localfile);
  1834       // create a tag describing this plugin.
  1835       PRLibrary *library = nullptr;
  1836       nsPluginInfo info;
  1837       memset(&info, 0, sizeof(info));
  1838       nsresult res;
  1839       // Opening a block for the telemetry AutoTimer
  1841         Telemetry::AutoTimer<Telemetry::PLUGIN_LOAD_METADATA> telemetry;
  1842         res = pluginFile.GetPluginInfo(info, &library);
  1844       // if we don't have mime type don't proceed, this is not a plugin
  1845       if (NS_FAILED(res) || !info.fMimeTypeArray) {
  1846         nsRefPtr<nsInvalidPluginTag> invalidTag = new nsInvalidPluginTag(filePath.get(),
  1847                                                                          fileModTime);
  1848         pluginFile.FreePluginInfo(info);
  1850         if (aCreatePluginList) {
  1851           invalidTag->mSeen = true;
  1853         invalidTag->mNext = mInvalidPlugins;
  1854         if (mInvalidPlugins) {
  1855           mInvalidPlugins->mPrev = invalidTag;
  1857         mInvalidPlugins = invalidTag;
  1859         // Mark aPluginsChanged so pluginreg is rewritten
  1860         *aPluginsChanged = true;
  1861         continue;
  1864       pluginTag = new nsPluginTag(&info, fileModTime, fromExtension);
  1865       pluginFile.FreePluginInfo(info);
  1866       if (!pluginTag)
  1867         return NS_ERROR_OUT_OF_MEMORY;
  1869       pluginTag->mLibrary = library;
  1870       uint32_t state = pluginTag->GetBlocklistState();
  1872       // If the blocklist says it is risky and we have never seen this
  1873       // plugin before, then disable it.
  1874       // If the blocklist says this is an outdated plugin, warn about
  1875       // outdated plugins.
  1876       if (state == nsIBlocklistService::STATE_SOFTBLOCKED && !seenBefore) {
  1877         pluginTag->SetEnabledState(nsIPluginTag::STATE_DISABLED);
  1879       if (state == nsIBlocklistService::STATE_OUTDATED && !seenBefore) {
  1880         warnOutdated = true;
  1883       // Plugin unloading is tag-based. If we created a new tag and loaded
  1884       // the library in the process then we want to attempt to unload it here.
  1885       // Only do this if the pref is set for aggressive unloading.
  1886       if (UnloadPluginsASAP()) {
  1887         pluginTag->TryUnloadPlugin(false);
  1891     // do it if we still want it
  1892     if (!seenBefore) {
  1893       // We have a valid new plugin so report that plugins have changed.
  1894       *aPluginsChanged = true;
  1897     // Avoid adding different versions of the same plugin if they are running 
  1898     // in-process, otherwise we risk undefined behaviour.
  1899     if (!nsNPAPIPlugin::RunPluginOOP(pluginTag)) {
  1900       if (HaveSamePlugin(pluginTag)) {
  1901         continue;
  1905     // Don't add the same plugin again if it hasn't changed
  1906     if (nsPluginTag* duplicate = FirstPluginWithPath(pluginTag->mFullPath)) {
  1907       if (pluginTag->mLastModifiedTime == duplicate->mLastModifiedTime) {
  1908         continue;
  1912     // If we're not creating a plugin list, simply looking for changes,
  1913     // then we're done.
  1914     if (!aCreatePluginList) {
  1915       return NS_OK;
  1918     // Add plugin tags such that the list is ordered by modification date,
  1919     // newest to oldest. This is ugly, it'd be easier with just about anything
  1920     // other than a single-directional linked list.
  1921     if (mPlugins) {
  1922       nsPluginTag *prev = nullptr;
  1923       nsPluginTag *next = mPlugins;
  1924       while (next) {
  1925         if (pluginTag->mLastModifiedTime >= next->mLastModifiedTime) {
  1926           pluginTag->mNext = next;
  1927           if (prev) {
  1928             prev->mNext = pluginTag;
  1929           } else {
  1930             mPlugins = pluginTag;
  1932           break;
  1934         prev = next;
  1935         next = prev->mNext;
  1936         if (!next) {
  1937           prev->mNext = pluginTag;
  1940     } else {
  1941       mPlugins = pluginTag;
  1944     if (pluginTag->IsActive()) {
  1945       nsAdoptingCString disableFullPage =
  1946         Preferences::GetCString(kPrefDisableFullPage);
  1947       for (uint32_t i = 0; i < pluginTag->mMimeTypes.Length(); i++) {
  1948         if (!IsTypeInList(pluginTag->mMimeTypes[i], disableFullPage)) {
  1949           RegisterWithCategoryManager(pluginTag->mMimeTypes[i],
  1950                                       ePluginRegister);
  1956   if (warnOutdated) {
  1957     Preferences::SetBool("plugins.update.notifyUser", true);
  1960   return NS_OK;
  1963 nsresult nsPluginHost::ScanPluginsDirectoryList(nsISimpleEnumerator *dirEnum,
  1964                                                 bool aCreatePluginList,
  1965                                                 bool *aPluginsChanged)
  1967     bool hasMore;
  1968     while (NS_SUCCEEDED(dirEnum->HasMoreElements(&hasMore)) && hasMore) {
  1969       nsCOMPtr<nsISupports> supports;
  1970       nsresult rv = dirEnum->GetNext(getter_AddRefs(supports));
  1971       if (NS_FAILED(rv))
  1972         continue;
  1973       nsCOMPtr<nsIFile> nextDir(do_QueryInterface(supports, &rv));
  1974       if (NS_FAILED(rv))
  1975         continue;
  1977       // don't pass aPluginsChanged directly to prevent it from been reset
  1978       bool pluginschanged = false;
  1979       ScanPluginsDirectory(nextDir, aCreatePluginList, &pluginschanged);
  1981       if (pluginschanged)
  1982         *aPluginsChanged = true;
  1984       // if changes are detected and we are not creating the list, do not proceed
  1985       if (!aCreatePluginList && *aPluginsChanged)
  1986         break;
  1988     return NS_OK;
  1991 nsresult nsPluginHost::LoadPlugins()
  1993 #ifdef ANDROID
  1994   if (XRE_GetProcessType() == GeckoProcessType_Content) {
  1995     return NS_OK;
  1997 #endif
  1998   // do not do anything if it is already done
  1999   // use ReloadPlugins() to enforce loading
  2000   if (mPluginsLoaded)
  2001     return NS_OK;
  2003   if (mPluginsDisabled)
  2004     return NS_OK;
  2006   bool pluginschanged;
  2007   nsresult rv = FindPlugins(true, &pluginschanged);
  2008   if (NS_FAILED(rv))
  2009     return rv;
  2011   // only if plugins have changed will we notify plugin-change observers
  2012   if (pluginschanged) {
  2013     nsCOMPtr<nsIObserverService> obsService =
  2014       mozilla::services::GetObserverService();
  2015     if (obsService)
  2016       obsService->NotifyObservers(nullptr, "plugins-list-updated", nullptr);
  2019   return NS_OK;
  2022 // if aCreatePluginList is false we will just scan for plugins
  2023 // and see if any changes have been made to the plugins.
  2024 // This is needed in ReloadPlugins to prevent possible recursive reloads
  2025 nsresult nsPluginHost::FindPlugins(bool aCreatePluginList, bool * aPluginsChanged)
  2027   Telemetry::AutoTimer<Telemetry::FIND_PLUGINS> telemetry;
  2029   NS_ENSURE_ARG_POINTER(aPluginsChanged);
  2031   *aPluginsChanged = false;
  2032   nsresult rv;
  2034   // Read cached plugins info. If the profile isn't yet available then don't
  2035   // scan for plugins
  2036   if (ReadPluginInfo() == NS_ERROR_NOT_AVAILABLE)
  2037     return NS_OK;
  2039 #ifdef XP_WIN
  2040   // Failure here is not a show-stopper so just warn.
  2041   rv = EnsurePrivateDirServiceProvider();
  2042   NS_ASSERTION(NS_SUCCEEDED(rv), "Failed to register dir service provider.");
  2043 #endif /* XP_WIN */
  2045   nsCOMPtr<nsIProperties> dirService(do_GetService(kDirectoryServiceContractID, &rv));
  2046   if (NS_FAILED(rv))
  2047     return rv;
  2049   nsCOMPtr<nsISimpleEnumerator> dirList;
  2051   // Scan plugins directories;
  2052   // don't pass aPluginsChanged directly, to prevent its
  2053   // possible reset in subsequent ScanPluginsDirectory calls
  2054   bool pluginschanged = false;
  2056   // Scan the app-defined list of plugin dirs.
  2057   rv = dirService->Get(NS_APP_PLUGINS_DIR_LIST, NS_GET_IID(nsISimpleEnumerator), getter_AddRefs(dirList));
  2058   if (NS_SUCCEEDED(rv)) {
  2059     ScanPluginsDirectoryList(dirList, aCreatePluginList, &pluginschanged);
  2061     if (pluginschanged)
  2062       *aPluginsChanged = true;
  2064     // if we are just looking for possible changes,
  2065     // no need to proceed if changes are detected
  2066     if (!aCreatePluginList && *aPluginsChanged) {
  2067       NS_ITERATIVE_UNREF_LIST(nsRefPtr<nsPluginTag>, mCachedPlugins, mNext);
  2068       NS_ITERATIVE_UNREF_LIST(nsRefPtr<nsInvalidPluginTag>, mInvalidPlugins, mNext);
  2069       return NS_OK;
  2071   } else {
  2072 #ifdef ANDROID
  2073     LOG("getting plugins dir failed");
  2074 #endif
  2077   mPluginsLoaded = true; // at this point 'some' plugins have been loaded,
  2078                             // the rest is optional
  2080 #ifdef XP_WIN
  2081   bool bScanPLIDs = Preferences::GetBool("plugin.scan.plid.all", false);
  2083     // Now lets scan any PLID directories
  2084   if (bScanPLIDs && mPrivateDirServiceProvider) {
  2085     rv = mPrivateDirServiceProvider->GetPLIDDirectories(getter_AddRefs(dirList));
  2086     if (NS_SUCCEEDED(rv)) {
  2087       ScanPluginsDirectoryList(dirList, aCreatePluginList, &pluginschanged);
  2089       if (pluginschanged)
  2090         *aPluginsChanged = true;
  2092       // if we are just looking for possible changes,
  2093       // no need to proceed if changes are detected
  2094       if (!aCreatePluginList && *aPluginsChanged) {
  2095         NS_ITERATIVE_UNREF_LIST(nsRefPtr<nsPluginTag>, mCachedPlugins, mNext);
  2096         NS_ITERATIVE_UNREF_LIST(nsRefPtr<nsInvalidPluginTag>, mInvalidPlugins, mNext);
  2097         return NS_OK;
  2103   // Scan the installation paths of our popular plugins if the prefs are enabled
  2105   // This table controls the order of scanning
  2106   const char* const prefs[] = {NS_WIN_ACROBAT_SCAN_KEY,
  2107                                NS_WIN_QUICKTIME_SCAN_KEY,
  2108                                NS_WIN_WMP_SCAN_KEY};
  2110   uint32_t size = sizeof(prefs) / sizeof(prefs[0]);
  2112   for (uint32_t i = 0; i < size; i+=1) {
  2113     nsCOMPtr<nsIFile> dirToScan;
  2114     bool bExists;
  2115     if (NS_SUCCEEDED(dirService->Get(prefs[i], NS_GET_IID(nsIFile), getter_AddRefs(dirToScan))) &&
  2116         dirToScan &&
  2117         NS_SUCCEEDED(dirToScan->Exists(&bExists)) &&
  2118         bExists) {
  2120       ScanPluginsDirectory(dirToScan, aCreatePluginList, &pluginschanged);
  2122       if (pluginschanged)
  2123         *aPluginsChanged = true;
  2125       // if we are just looking for possible changes,
  2126       // no need to proceed if changes are detected
  2127       if (!aCreatePluginList && *aPluginsChanged) {
  2128         NS_ITERATIVE_UNREF_LIST(nsRefPtr<nsPluginTag>, mCachedPlugins, mNext);
  2129         NS_ITERATIVE_UNREF_LIST(nsRefPtr<nsInvalidPluginTag>, mInvalidPlugins, mNext);
  2130         return NS_OK;
  2134 #endif
  2136   // We should also consider plugins to have changed if any plugins have been removed.
  2137   // We'll know if any were removed if they weren't taken out of the cached plugins list
  2138   // during our scan, thus we can assume something was removed if the cached plugins list
  2139   // contains anything.
  2140   if (!*aPluginsChanged && mCachedPlugins) {
  2141     *aPluginsChanged = true;
  2144   // Remove unseen invalid plugins
  2145   nsRefPtr<nsInvalidPluginTag> invalidPlugins = mInvalidPlugins;
  2146   while (invalidPlugins) {
  2147     if (!invalidPlugins->mSeen) {
  2148       nsRefPtr<nsInvalidPluginTag> invalidPlugin = invalidPlugins;
  2150       if (invalidPlugin->mPrev) {
  2151         invalidPlugin->mPrev->mNext = invalidPlugin->mNext;
  2153       else {
  2154         mInvalidPlugins = invalidPlugin->mNext;
  2156       if (invalidPlugin->mNext) {
  2157         invalidPlugin->mNext->mPrev = invalidPlugin->mPrev;
  2160       invalidPlugins = invalidPlugin->mNext;
  2162       invalidPlugin->mPrev = nullptr;
  2163       invalidPlugin->mNext = nullptr;
  2165     else {
  2166       invalidPlugins->mSeen = false;
  2167       invalidPlugins = invalidPlugins->mNext;
  2171   // if we are not creating the list, there is no need to proceed
  2172   if (!aCreatePluginList) {
  2173     NS_ITERATIVE_UNREF_LIST(nsRefPtr<nsPluginTag>, mCachedPlugins, mNext);
  2174     NS_ITERATIVE_UNREF_LIST(nsRefPtr<nsInvalidPluginTag>, mInvalidPlugins, mNext);
  2175     return NS_OK;
  2178   // if we are creating the list, it is already done;
  2179   // update the plugins info cache if changes are detected
  2180   if (*aPluginsChanged)
  2181     WritePluginInfo();
  2183   // No more need for cached plugins. Clear it up.
  2184   NS_ITERATIVE_UNREF_LIST(nsRefPtr<nsPluginTag>, mCachedPlugins, mNext);
  2185   NS_ITERATIVE_UNREF_LIST(nsRefPtr<nsInvalidPluginTag>, mInvalidPlugins, mNext);
  2187   return NS_OK;
  2190 nsresult
  2191 nsPluginHost::UpdatePluginInfo(nsPluginTag* aPluginTag)
  2193   ReadPluginInfo();
  2194   WritePluginInfo();
  2195   NS_ITERATIVE_UNREF_LIST(nsRefPtr<nsPluginTag>, mCachedPlugins, mNext);
  2196   NS_ITERATIVE_UNREF_LIST(nsRefPtr<nsInvalidPluginTag>, mInvalidPlugins, mNext);
  2198   if (!aPluginTag) {
  2199     return NS_OK;
  2202   // Update types with category manager
  2203   nsAdoptingCString disableFullPage =
  2204     Preferences::GetCString(kPrefDisableFullPage);
  2205   for (uint32_t i = 0; i < aPluginTag->mMimeTypes.Length(); i++) {
  2206     nsRegisterType shouldRegister;
  2208     if (IsTypeInList(aPluginTag->mMimeTypes[i], disableFullPage)) {
  2209       shouldRegister = ePluginUnregister;
  2210     } else {
  2211       nsPluginTag *plugin = FindPluginForType(aPluginTag->mMimeTypes[i].get(),
  2212                                               true);
  2213       shouldRegister = plugin ? ePluginRegister : ePluginUnregister;
  2216     RegisterWithCategoryManager(aPluginTag->mMimeTypes[i], shouldRegister);
  2219   nsCOMPtr<nsIObserverService> obsService =
  2220     mozilla::services::GetObserverService();
  2221   if (obsService)
  2222     obsService->NotifyObservers(nullptr, "plugin-info-updated", nullptr);
  2224   // Reload instances if needed
  2225   if (aPluginTag->IsActive()) {
  2226     return NS_OK;
  2229   return NS_OK;
  2232 /* static */ bool
  2233 nsPluginHost::IsTypeWhitelisted(const char *aMimeType)
  2235   nsAdoptingCString whitelist = Preferences::GetCString(kPrefWhitelist);
  2236   if (!whitelist.Length()) {
  2237     return true;
  2239   nsDependentCString wrap(aMimeType);
  2240   return IsTypeInList(wrap, whitelist);
  2243 void
  2244 nsPluginHost::RegisterWithCategoryManager(nsCString &aMimeType,
  2245                                           nsRegisterType aType)
  2247   PLUGIN_LOG(PLUGIN_LOG_NORMAL,
  2248              ("nsPluginTag::RegisterWithCategoryManager type = %s, removing = %s\n",
  2249               aMimeType.get(), aType == ePluginUnregister ? "yes" : "no"));
  2251   nsCOMPtr<nsICategoryManager> catMan =
  2252     do_GetService(NS_CATEGORYMANAGER_CONTRACTID);
  2253   if (!catMan) {
  2254     return;
  2257   const char *contractId =
  2258     "@mozilla.org/content/plugin/document-loader-factory;1";
  2260   if (aType == ePluginRegister) {
  2261     catMan->AddCategoryEntry("Gecko-Content-Viewers",
  2262                              aMimeType.get(),
  2263                              contractId,
  2264                              false, /* persist: broken by bug 193031 */
  2265                              mOverrideInternalTypes,
  2266                              nullptr);
  2267   } else {
  2268     // Only delete the entry if a plugin registered for it
  2269     nsXPIDLCString value;
  2270     nsresult rv = catMan->GetCategoryEntry("Gecko-Content-Viewers",
  2271                                            aMimeType.get(),
  2272                                            getter_Copies(value));
  2273     if (NS_SUCCEEDED(rv) && strcmp(value, contractId) == 0) {
  2274       catMan->DeleteCategoryEntry("Gecko-Content-Viewers",
  2275                                   aMimeType.get(),
  2276                                   true);
  2281 nsresult
  2282 nsPluginHost::WritePluginInfo()
  2285   nsresult rv = NS_OK;
  2286   nsCOMPtr<nsIProperties> directoryService(do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID,&rv));
  2287   if (NS_FAILED(rv))
  2288     return rv;
  2290   directoryService->Get(NS_APP_USER_PROFILE_50_DIR, NS_GET_IID(nsIFile),
  2291                         getter_AddRefs(mPluginRegFile));
  2293   if (!mPluginRegFile)
  2294     return NS_ERROR_FAILURE;
  2296   PRFileDesc* fd = nullptr;
  2298   nsCOMPtr<nsIFile> pluginReg;
  2300   rv = mPluginRegFile->Clone(getter_AddRefs(pluginReg));
  2301   if (NS_FAILED(rv))
  2302     return rv;
  2304   nsAutoCString filename(kPluginRegistryFilename);
  2305   filename.Append(".tmp");
  2306   rv = pluginReg->AppendNative(filename);
  2307   if (NS_FAILED(rv))
  2308     return rv;
  2310   rv = pluginReg->OpenNSPRFileDesc(PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE, 0600, &fd);
  2311   if (NS_FAILED(rv))
  2312     return rv;
  2314   nsCOMPtr<nsIXULRuntime> runtime = do_GetService("@mozilla.org/xre/runtime;1");
  2315   if (!runtime) {
  2316     return NS_ERROR_FAILURE;
  2319   nsAutoCString arch;
  2320   rv = runtime->GetXPCOMABI(arch);
  2321   if (NS_FAILED(rv)) {
  2322     return rv;
  2325   PR_fprintf(fd, "Generated File. Do not edit.\n");
  2327   PR_fprintf(fd, "\n[HEADER]\nVersion%c%s%c%c\nArch%c%s%c%c\n",
  2328              PLUGIN_REGISTRY_FIELD_DELIMITER,
  2329              kPluginRegistryVersion,
  2330              PLUGIN_REGISTRY_FIELD_DELIMITER,
  2331              PLUGIN_REGISTRY_END_OF_LINE_MARKER,
  2332              PLUGIN_REGISTRY_FIELD_DELIMITER,
  2333              arch.get(),
  2334              PLUGIN_REGISTRY_FIELD_DELIMITER,
  2335              PLUGIN_REGISTRY_END_OF_LINE_MARKER);
  2337   // Store all plugins in the mPlugins list - all plugins currently in use.
  2338   PR_fprintf(fd, "\n[PLUGINS]\n");
  2340   for (nsPluginTag *tag = mPlugins; tag; tag = tag->mNext) {
  2341     // store each plugin info into the registry
  2342     // filename & fullpath are on separate line
  2343     // because they can contain field delimiter char
  2344     PR_fprintf(fd, "%s%c%c\n%s%c%c\n%s%c%c\n",
  2345       (tag->mFileName.get()),
  2346       PLUGIN_REGISTRY_FIELD_DELIMITER,
  2347       PLUGIN_REGISTRY_END_OF_LINE_MARKER,
  2348       (tag->mFullPath.get()),
  2349       PLUGIN_REGISTRY_FIELD_DELIMITER,
  2350       PLUGIN_REGISTRY_END_OF_LINE_MARKER,
  2351       (tag->mVersion.get()),
  2352       PLUGIN_REGISTRY_FIELD_DELIMITER,
  2353       PLUGIN_REGISTRY_END_OF_LINE_MARKER);
  2355     // lastModifiedTimeStamp|canUnload|tag->mFlags|fromExtension
  2356     PR_fprintf(fd, "%lld%c%d%c%lu%c%d%c%c\n",
  2357       tag->mLastModifiedTime,
  2358       PLUGIN_REGISTRY_FIELD_DELIMITER,
  2359       false, // did store whether or not to unload in-process plugins
  2360       PLUGIN_REGISTRY_FIELD_DELIMITER,
  2361       0, // legacy field for flags
  2362       PLUGIN_REGISTRY_FIELD_DELIMITER,
  2363       tag->IsFromExtension(),
  2364       PLUGIN_REGISTRY_FIELD_DELIMITER,
  2365       PLUGIN_REGISTRY_END_OF_LINE_MARKER);
  2367     //description, name & mtypecount are on separate line
  2368     PR_fprintf(fd, "%s%c%c\n%s%c%c\n%d\n",
  2369       (tag->mDescription.get()),
  2370       PLUGIN_REGISTRY_FIELD_DELIMITER,
  2371       PLUGIN_REGISTRY_END_OF_LINE_MARKER,
  2372       (tag->mName.get()),
  2373       PLUGIN_REGISTRY_FIELD_DELIMITER,
  2374       PLUGIN_REGISTRY_END_OF_LINE_MARKER,
  2375       tag->mMimeTypes.Length());
  2377     // Add in each mimetype this plugin supports
  2378     for (uint32_t i = 0; i < tag->mMimeTypes.Length(); i++) {
  2379       PR_fprintf(fd, "%d%c%s%c%s%c%s%c%c\n",
  2380         i,PLUGIN_REGISTRY_FIELD_DELIMITER,
  2381         (tag->mMimeTypes[i].get()),
  2382         PLUGIN_REGISTRY_FIELD_DELIMITER,
  2383         (tag->mMimeDescriptions[i].get()),
  2384         PLUGIN_REGISTRY_FIELD_DELIMITER,
  2385         (tag->mExtensions[i].get()),
  2386         PLUGIN_REGISTRY_FIELD_DELIMITER,
  2387         PLUGIN_REGISTRY_END_OF_LINE_MARKER);
  2391   PR_fprintf(fd, "\n[INVALID]\n");
  2393   nsRefPtr<nsInvalidPluginTag> invalidPlugins = mInvalidPlugins;
  2394   while (invalidPlugins) {
  2395     // fullPath
  2396     PR_fprintf(fd, "%s%c%c\n",
  2397       (!invalidPlugins->mFullPath.IsEmpty() ? invalidPlugins->mFullPath.get() : ""),
  2398       PLUGIN_REGISTRY_FIELD_DELIMITER,
  2399       PLUGIN_REGISTRY_END_OF_LINE_MARKER);
  2401     // lastModifiedTimeStamp
  2402     PR_fprintf(fd, "%lld%c%c\n",
  2403       invalidPlugins->mLastModifiedTime,
  2404       PLUGIN_REGISTRY_FIELD_DELIMITER,
  2405       PLUGIN_REGISTRY_END_OF_LINE_MARKER);
  2407     invalidPlugins = invalidPlugins->mNext;
  2410   PRStatus prrc;
  2411   prrc = PR_Close(fd);
  2412   if (prrc != PR_SUCCESS) {
  2413     // we should obtain a refined value based on prrc;
  2414     rv = NS_ERROR_FAILURE;
  2415     MOZ_ASSERT(false, "PR_Close() failed.");
  2416     return rv;
  2418   nsCOMPtr<nsIFile> parent;
  2419   rv = pluginReg->GetParent(getter_AddRefs(parent));
  2420   NS_ENSURE_SUCCESS(rv, rv);
  2421   rv = pluginReg->MoveToNative(parent, kPluginRegistryFilename);
  2422   return rv;
  2425 nsresult
  2426 nsPluginHost::ReadPluginInfo()
  2428   const long PLUGIN_REG_MIMETYPES_ARRAY_SIZE = 12;
  2429   const long PLUGIN_REG_MAX_MIMETYPES = 1000;
  2431   // we need to import the legacy flags from the plugin registry once
  2432   const bool pluginStateImported =
  2433     Preferences::GetDefaultBool("plugin.importedState", false);
  2435   nsresult rv;
  2437   nsCOMPtr<nsIProperties> directoryService(do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID,&rv));
  2438   if (NS_FAILED(rv))
  2439     return rv;
  2441   directoryService->Get(NS_APP_USER_PROFILE_50_DIR, NS_GET_IID(nsIFile),
  2442                         getter_AddRefs(mPluginRegFile));
  2444   if (!mPluginRegFile) {
  2445     // There is no profile yet, this will tell us if there is going to be one
  2446     // in the future.
  2447     directoryService->Get(NS_APP_PROFILE_DIR_STARTUP, NS_GET_IID(nsIFile),
  2448                           getter_AddRefs(mPluginRegFile));
  2449     if (!mPluginRegFile)
  2450       return NS_ERROR_FAILURE;
  2451     else
  2452       return NS_ERROR_NOT_AVAILABLE;
  2455   PRFileDesc* fd = nullptr;
  2457   nsCOMPtr<nsIFile> pluginReg;
  2459   rv = mPluginRegFile->Clone(getter_AddRefs(pluginReg));
  2460   if (NS_FAILED(rv))
  2461     return rv;
  2463   rv = pluginReg->AppendNative(kPluginRegistryFilename);
  2464   if (NS_FAILED(rv))
  2465     return rv;
  2467   int64_t fileSize;
  2468   rv = pluginReg->GetFileSize(&fileSize);
  2469   if (NS_FAILED(rv))
  2470     return rv;
  2472   if (fileSize > INT32_MAX) {
  2473     return NS_ERROR_FAILURE;
  2475   int32_t flen = int32_t(fileSize);
  2476   if (flen == 0) {
  2477     NS_WARNING("Plugins Registry Empty!");
  2478     return NS_OK; // ERROR CONDITION
  2481   nsPluginManifestLineReader reader;
  2482   char* registry = reader.Init(flen);
  2483   if (!registry)
  2484     return NS_ERROR_OUT_OF_MEMORY;
  2486   rv = pluginReg->OpenNSPRFileDesc(PR_RDONLY, 0444, &fd);
  2487   if (NS_FAILED(rv))
  2488     return rv;
  2490   // set rv to return an error on goto out
  2491   rv = NS_ERROR_FAILURE;
  2493   int32_t bread = PR_Read(fd, registry, flen);
  2495   PRStatus prrc;
  2496   prrc = PR_Close(fd);
  2497   if (prrc != PR_SUCCESS) {
  2498     // Strange error: this is one of those "Should not happen" error.
  2499     // we may want to report something more refined than  NS_ERROR_FAILURE.
  2500     MOZ_ASSERT(false, "PR_Close() failed.");
  2501     return rv;
  2504   if (flen > bread)
  2505     return rv;
  2507   if (!ReadSectionHeader(reader, "HEADER"))
  2508     return rv;;
  2510   if (!reader.NextLine())
  2511     return rv;
  2513   char* values[6];
  2515   // VersionLiteral, kPluginRegistryVersion
  2516   if (2 != reader.ParseLine(values, 2))
  2517     return rv;
  2519   // VersionLiteral
  2520   if (PL_strcmp(values[0], "Version"))
  2521     return rv;
  2523   // kPluginRegistryVersion
  2524   int32_t vdiff = mozilla::CompareVersions(values[1], kPluginRegistryVersion);
  2525   mozilla::Version version(values[1]);
  2526   // If this is a registry from some future version then don't attempt to read it
  2527   if (vdiff > 0)
  2528     return rv;
  2529   // If this is a registry from before the minimum then don't attempt to read it
  2530   if (version < kMinimumRegistryVersion)
  2531     return rv;
  2533   // Registry v0.10 and upwards includes the plugin version field
  2534   bool regHasVersion = (version >= "0.10");
  2536   // Registry v0.13 and upwards includes the architecture
  2537   if (version >= "0.13") {
  2538     char* archValues[6];
  2540     if (!reader.NextLine()) {
  2541       return rv;
  2544     // ArchLiteral, Architecture
  2545     if (2 != reader.ParseLine(archValues, 2)) {
  2546       return rv;
  2549     // ArchLiteral
  2550     if (PL_strcmp(archValues[0], "Arch")) {
  2551       return rv;
  2554     nsCOMPtr<nsIXULRuntime> runtime = do_GetService("@mozilla.org/xre/runtime;1");
  2555     if (!runtime) {
  2556       return rv;
  2559     nsAutoCString arch;
  2560     if (NS_FAILED(runtime->GetXPCOMABI(arch))) {
  2561       return rv;
  2564     // If this is a registry from a different architecture then don't attempt to read it
  2565     if (PL_strcmp(archValues[1], arch.get())) {
  2566       return rv;
  2570   // Registry v0.13 and upwards includes the list of invalid plugins
  2571   const bool hasInvalidPlugins = (version >= "0.13");
  2573   // Registry v0.16 and upwards always have 0 for their plugin flags, prefs are used instead
  2574   const bool hasValidFlags = (version < "0.16");
  2576   // Registry v0.17 and upwards store whether the plugin comes from an XPI.
  2577   const bool hasFromExtension = (version >= "0.17");
  2579 #if defined(XP_MACOSX)
  2580   const bool hasFullPathInFileNameField = false;
  2581 #else
  2582   const bool hasFullPathInFileNameField = (version < "0.11");
  2583 #endif
  2585   if (!ReadSectionHeader(reader, "PLUGINS"))
  2586     return rv;
  2588   while (reader.NextLine()) {
  2589     const char *filename;
  2590     const char *fullpath;
  2591     nsAutoCString derivedFileName;
  2593     if (hasInvalidPlugins && *reader.LinePtr() == '[') {
  2594       break;
  2597     if (hasFullPathInFileNameField) {
  2598       fullpath = reader.LinePtr();
  2599       if (!reader.NextLine())
  2600         return rv;
  2601       // try to derive a file name from the full path
  2602       if (fullpath) {
  2603         nsCOMPtr<nsIFile> file = do_CreateInstance("@mozilla.org/file/local;1");
  2604         file->InitWithNativePath(nsDependentCString(fullpath));
  2605         file->GetNativeLeafName(derivedFileName);
  2606         filename = derivedFileName.get();
  2607       } else {
  2608         filename = nullptr;
  2611       // skip the next line, useless in this version
  2612       if (!reader.NextLine())
  2613         return rv;
  2614     } else {
  2615       filename = reader.LinePtr();
  2616       if (!reader.NextLine())
  2617         return rv;
  2619       fullpath = reader.LinePtr();
  2620       if (!reader.NextLine())
  2621         return rv;
  2624     const char *version;
  2625     if (regHasVersion) {
  2626       version = reader.LinePtr();
  2627       if (!reader.NextLine())
  2628         return rv;
  2629     } else {
  2630       version = "0";
  2633     // lastModifiedTimeStamp|canUnload|tag.mFlag|fromExtension
  2634     const int count = hasFromExtension ? 4 : 3;
  2635     if (reader.ParseLine(values, count) != count)
  2636       return rv;
  2638     // If this is an old plugin registry mark this plugin tag to be refreshed
  2639     int64_t lastmod = (vdiff == 0) ? nsCRT::atoll(values[0]) : -1;
  2640     uint32_t tagflag = atoi(values[2]);
  2641     bool fromExtension = false;
  2642     if (hasFromExtension) {
  2643       fromExtension = atoi(values[3]);
  2645     if (!reader.NextLine())
  2646       return rv;
  2648     char *description = reader.LinePtr();
  2649     if (!reader.NextLine())
  2650       return rv;
  2652 #if MOZ_WIDGET_ANDROID
  2653     // Flash on Android does not populate the version field, but it is tacked on to the description.
  2654     // For example, "Shockwave Flash 11.1 r115"
  2655     if (PL_strncmp("Shockwave Flash ", description, 16) == 0 && description[16]) {
  2656       version = &description[16];
  2658 #endif
  2660     const char *name = reader.LinePtr();
  2661     if (!reader.NextLine())
  2662       return rv;
  2664     long mimetypecount = std::strtol(reader.LinePtr(), nullptr, 10);
  2665     if (mimetypecount == LONG_MAX || mimetypecount == LONG_MIN ||
  2666         mimetypecount >= PLUGIN_REG_MAX_MIMETYPES || mimetypecount < 0) {
  2667       return NS_ERROR_FAILURE;
  2670     char *stackalloced[PLUGIN_REG_MIMETYPES_ARRAY_SIZE * 3];
  2671     char **mimetypes;
  2672     char **mimedescriptions;
  2673     char **extensions;
  2674     char **heapalloced = 0;
  2675     if (mimetypecount > PLUGIN_REG_MIMETYPES_ARRAY_SIZE - 1) {
  2676       heapalloced = new char *[mimetypecount * 3];
  2677       mimetypes = heapalloced;
  2678     } else {
  2679       mimetypes = stackalloced;
  2681     mimedescriptions = mimetypes + mimetypecount;
  2682     extensions = mimedescriptions + mimetypecount;
  2684     int mtr = 0; //mimetype read
  2685     for (; mtr < mimetypecount; mtr++) {
  2686       if (!reader.NextLine())
  2687         break;
  2689       //line number|mimetype|description|extension
  2690       if (4 != reader.ParseLine(values, 4))
  2691         break;
  2692       int line = atoi(values[0]);
  2693       if (line != mtr)
  2694         break;
  2695       mimetypes[mtr] = values[1];
  2696       mimedescriptions[mtr] = values[2];
  2697       extensions[mtr] = values[3];
  2700     if (mtr != mimetypecount) {
  2701       if (heapalloced) {
  2702         delete [] heapalloced;
  2704       return rv;
  2707     nsRefPtr<nsPluginTag> tag = new nsPluginTag(name,
  2708       description,
  2709       filename,
  2710       fullpath,
  2711       version,
  2712       (const char* const*)mimetypes,
  2713       (const char* const*)mimedescriptions,
  2714       (const char* const*)extensions,
  2715       mimetypecount, lastmod, fromExtension, true);
  2716     if (heapalloced)
  2717       delete [] heapalloced;
  2719     // Import flags from registry into prefs for old registry versions
  2720     if (hasValidFlags && !pluginStateImported) {
  2721       tag->ImportFlagsToPrefs(tagflag);
  2724     PR_LOG(nsPluginLogging::gPluginLog, PLUGIN_LOG_BASIC,
  2725       ("LoadCachedPluginsInfo : Loading Cached plugininfo for %s\n", tag->mFileName.get()));
  2726     tag->mNext = mCachedPlugins;
  2727     mCachedPlugins = tag;
  2730 // On Android we always want to try to load a plugin again (Flash). Bug 935676.
  2731 #ifndef MOZ_WIDGET_ANDROID
  2732   if (hasInvalidPlugins) {
  2733     if (!ReadSectionHeader(reader, "INVALID")) {
  2734       return rv;
  2737     while (reader.NextLine()) {
  2738       const char *fullpath = reader.LinePtr();
  2739       if (!reader.NextLine()) {
  2740         return rv;
  2743       const char *lastModifiedTimeStamp = reader.LinePtr();
  2744       int64_t lastmod = (vdiff == 0) ? nsCRT::atoll(lastModifiedTimeStamp) : -1;
  2746       nsRefPtr<nsInvalidPluginTag> invalidTag = new nsInvalidPluginTag(fullpath, lastmod);
  2748       invalidTag->mNext = mInvalidPlugins;
  2749       if (mInvalidPlugins) {
  2750         mInvalidPlugins->mPrev = invalidTag;
  2752       mInvalidPlugins = invalidTag;
  2755 #endif
  2757   // flip the pref so we don't import the legacy flags again
  2758   Preferences::SetBool("plugin.importedState", true);
  2760   return NS_OK;
  2763 void
  2764 nsPluginHost::RemoveCachedPluginsInfo(const char *filePath, nsPluginTag **result)
  2766   nsRefPtr<nsPluginTag> prev;
  2767   nsRefPtr<nsPluginTag> tag = mCachedPlugins;
  2768   while (tag)
  2770     if (tag->mFullPath.Equals(filePath)) {
  2771       // Found it. Remove it from our list
  2772       if (prev)
  2773         prev->mNext = tag->mNext;
  2774       else
  2775         mCachedPlugins = tag->mNext;
  2776       tag->mNext = nullptr;
  2777       *result = tag;
  2778       NS_ADDREF(*result);
  2779       break;
  2781     prev = tag;
  2782     tag = tag->mNext;
  2786 #ifdef XP_WIN
  2787 nsresult
  2788 nsPluginHost::EnsurePrivateDirServiceProvider()
  2790   if (!mPrivateDirServiceProvider) {
  2791     nsresult rv;
  2792     mPrivateDirServiceProvider = new nsPluginDirServiceProvider();
  2793     if (!mPrivateDirServiceProvider)
  2794       return NS_ERROR_OUT_OF_MEMORY;
  2795     nsCOMPtr<nsIDirectoryService> dirService(do_GetService(kDirectoryServiceContractID, &rv));
  2796     if (NS_FAILED(rv))
  2797       return rv;
  2798     rv = dirService->RegisterProvider(mPrivateDirServiceProvider);
  2799     if (NS_FAILED(rv))
  2800       return rv;
  2802   return NS_OK;
  2804 #endif /* XP_WIN */
  2806 nsresult nsPluginHost::NewPluginURLStream(const nsString& aURL,
  2807                                           nsNPAPIPluginInstance *aInstance,
  2808                                           nsNPAPIPluginStreamListener* aListener,
  2809                                           nsIInputStream *aPostStream,
  2810                                           const char *aHeadersData,
  2811                                           uint32_t aHeadersDataLen)
  2813   nsCOMPtr<nsIURI> url;
  2814   nsAutoString absUrl;
  2815   nsresult rv;
  2817   if (aURL.Length() <= 0)
  2818     return NS_OK;
  2820   // get the base URI for the plugin to create an absolute url 
  2821   // in case aURL is relative
  2822   nsRefPtr<nsPluginInstanceOwner> owner = aInstance->GetOwner();
  2823   if (owner) {
  2824     rv = NS_MakeAbsoluteURI(absUrl, aURL, nsCOMPtr<nsIURI>(owner->GetBaseURI()));
  2827   if (absUrl.IsEmpty())
  2828     absUrl.Assign(aURL);
  2830   rv = NS_NewURI(getter_AddRefs(url), absUrl);
  2831   if (NS_FAILED(rv))
  2832     return rv;
  2834   nsCOMPtr<nsIDOMElement> element;
  2835   nsCOMPtr<nsIDocument> doc;
  2836   if (owner) {
  2837     owner->GetDOMElement(getter_AddRefs(element));
  2838     owner->GetDocument(getter_AddRefs(doc));
  2841   int16_t shouldLoad = nsIContentPolicy::ACCEPT;
  2842   rv = NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_OBJECT_SUBREQUEST,
  2843                                  url,
  2844                                  (doc ? doc->NodePrincipal() : nullptr),
  2845                                  element,
  2846                                  EmptyCString(), //mime guess
  2847                                  nullptr,         //extra
  2848                                  &shouldLoad);
  2849   if (NS_FAILED(rv))
  2850     return rv;
  2851   if (NS_CP_REJECTED(shouldLoad)) {
  2852     // Disallowed by content policy
  2853     return NS_ERROR_CONTENT_BLOCKED;
  2856   nsRefPtr<nsPluginStreamListenerPeer> listenerPeer = new nsPluginStreamListenerPeer();
  2857   if (!listenerPeer)
  2858     return NS_ERROR_OUT_OF_MEMORY;
  2860   rv = listenerPeer->Initialize(url, aInstance, aListener);
  2861   if (NS_FAILED(rv))
  2862     return rv;
  2864   nsCOMPtr<nsIChannel> channel;
  2865   rv = NS_NewChannel(getter_AddRefs(channel), url, nullptr,
  2866     nullptr, /* do not add this internal plugin's channel
  2867             on the load group otherwise this channel could be canceled
  2868             form |nsDocShell::OnLinkClickSync| bug 166613 */
  2869     listenerPeer);
  2870   if (NS_FAILED(rv))
  2871     return rv;
  2873   if (doc) {
  2874     // Set the owner of channel to the document principal...
  2875     channel->SetOwner(doc->NodePrincipal());
  2877     // And if it's a script allow it to execute against the
  2878     // document's script context.
  2879     nsCOMPtr<nsIScriptChannel> scriptChannel(do_QueryInterface(channel));
  2880     if (scriptChannel) {
  2881       scriptChannel->SetExecutionPolicy(nsIScriptChannel::EXECUTE_NORMAL);
  2882       // Plug-ins seem to depend on javascript: URIs running synchronously
  2883       scriptChannel->SetExecuteAsync(false);
  2887   // deal with headers and post data
  2888   nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel));
  2889   if (httpChannel) {
  2890     if (!aPostStream) {
  2891       // Only set the Referer header for GET requests because IIS throws
  2892       // errors about malformed requests if we include it in POSTs. See
  2893       // bug 724465.
  2894       nsCOMPtr<nsIURI> referer;
  2896       nsCOMPtr<nsIObjectLoadingContent> olc = do_QueryInterface(element);
  2897       if (olc)
  2898         olc->GetSrcURI(getter_AddRefs(referer));
  2901       if (!referer) {
  2902         if (!doc) {
  2903           return NS_ERROR_FAILURE;
  2905         referer = doc->GetDocumentURI();
  2908       rv = httpChannel->SetReferrer(referer);
  2909       NS_ENSURE_SUCCESS(rv,rv);
  2912     if (aPostStream) {
  2913       // XXX it's a bit of a hack to rewind the postdata stream
  2914       // here but it has to be done in case the post data is
  2915       // being reused multiple times.
  2916       nsCOMPtr<nsISeekableStream>
  2917       postDataSeekable(do_QueryInterface(aPostStream));
  2918       if (postDataSeekable)
  2919         postDataSeekable->Seek(nsISeekableStream::NS_SEEK_SET, 0);
  2921       nsCOMPtr<nsIUploadChannel> uploadChannel(do_QueryInterface(httpChannel));
  2922       NS_ASSERTION(uploadChannel, "http must support nsIUploadChannel");
  2924       uploadChannel->SetUploadStream(aPostStream, EmptyCString(), -1);
  2927     if (aHeadersData) {
  2928       rv = AddHeadersToChannel(aHeadersData, aHeadersDataLen, httpChannel);
  2929       NS_ENSURE_SUCCESS(rv,rv);
  2932   rv = channel->AsyncOpen(listenerPeer, nullptr);
  2933   if (NS_SUCCEEDED(rv))
  2934     listenerPeer->TrackRequest(channel);
  2935   return rv;
  2938 // Called by GetURL and PostURL
  2939 nsresult
  2940 nsPluginHost::DoURLLoadSecurityCheck(nsNPAPIPluginInstance *aInstance,
  2941                                      const char* aURL)
  2943   if (!aURL || *aURL == '\0')
  2944     return NS_OK;
  2946   // get the base URI for the plugin element
  2947   nsRefPtr<nsPluginInstanceOwner> owner = aInstance->GetOwner();
  2948   if (!owner)
  2949     return NS_ERROR_FAILURE;
  2951   nsCOMPtr<nsIURI> baseURI = owner->GetBaseURI();
  2952   if (!baseURI)
  2953     return NS_ERROR_FAILURE;
  2955   // Create an absolute URL for the target in case the target is relative
  2956   nsCOMPtr<nsIURI> targetURL;
  2957   NS_NewURI(getter_AddRefs(targetURL), aURL, baseURI);
  2958   if (!targetURL)
  2959     return NS_ERROR_FAILURE;
  2961   nsCOMPtr<nsIDocument> doc;
  2962   owner->GetDocument(getter_AddRefs(doc));
  2963   if (!doc)
  2964     return NS_ERROR_FAILURE;
  2966   nsresult rv;
  2967   nsCOMPtr<nsIScriptSecurityManager> secMan(
  2968     do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv));
  2969   if (NS_FAILED(rv))
  2970     return rv;
  2972   return secMan->CheckLoadURIWithPrincipal(doc->NodePrincipal(), targetURL,
  2973                                            nsIScriptSecurityManager::STANDARD);
  2977 nsresult
  2978 nsPluginHost::AddHeadersToChannel(const char *aHeadersData,
  2979                                   uint32_t aHeadersDataLen,
  2980                                   nsIChannel *aGenericChannel)
  2982   nsresult rv = NS_OK;
  2984   nsCOMPtr<nsIHttpChannel> aChannel = do_QueryInterface(aGenericChannel);
  2985   if (!aChannel) {
  2986     return NS_ERROR_NULL_POINTER;
  2989   // used during the manipulation of the String from the aHeadersData
  2990   nsAutoCString headersString;
  2991   nsAutoCString oneHeader;
  2992   nsAutoCString headerName;
  2993   nsAutoCString headerValue;
  2994   int32_t crlf = 0;
  2995   int32_t colon = 0;
  2997   // Turn the char * buffer into an nsString.
  2998   headersString = aHeadersData;
  3000   // Iterate over the nsString: for each "\r\n" delimited chunk,
  3001   // add the value as a header to the nsIHTTPChannel
  3002   while (true) {
  3003     crlf = headersString.Find("\r\n", true);
  3004     if (-1 == crlf) {
  3005       rv = NS_OK;
  3006       return rv;
  3008     headersString.Mid(oneHeader, 0, crlf);
  3009     headersString.Cut(0, crlf + 2);
  3010     oneHeader.StripWhitespace();
  3011     colon = oneHeader.Find(":");
  3012     if (-1 == colon) {
  3013       rv = NS_ERROR_NULL_POINTER;
  3014       return rv;
  3016     oneHeader.Left(headerName, colon);
  3017     colon++;
  3018     oneHeader.Mid(headerValue, colon, oneHeader.Length() - colon);
  3020     // FINALLY: we can set the header!
  3022     rv = aChannel->SetRequestHeader(headerName, headerValue, true);
  3023     if (NS_FAILED(rv)) {
  3024       rv = NS_ERROR_NULL_POINTER;
  3025       return rv;
  3028   return rv;
  3031 nsresult
  3032 nsPluginHost::StopPluginInstance(nsNPAPIPluginInstance* aInstance)
  3034   if (PluginDestructionGuard::DelayDestroy(aInstance)) {
  3035     return NS_OK;
  3038   PLUGIN_LOG(PLUGIN_LOG_NORMAL,
  3039   ("nsPluginHost::StopPluginInstance called instance=%p\n",aInstance));
  3041   if (aInstance->HasStartedDestroying()) {
  3042     return NS_OK;
  3045   Telemetry::AutoTimer<Telemetry::PLUGIN_SHUTDOWN_MS> timer;
  3046   aInstance->Stop();
  3048   // if the instance does not want to be 'cached' just remove it
  3049   bool doCache = aInstance->ShouldCache();
  3050   if (doCache) {
  3051     // try to get the max cached instances from a pref or use default
  3052     uint32_t cachedInstanceLimit =
  3053       Preferences::GetUint(NS_PREF_MAX_NUM_CACHED_INSTANCES,
  3054                            DEFAULT_NUMBER_OF_STOPPED_INSTANCES);
  3055     if (StoppedInstanceCount() >= cachedInstanceLimit) {
  3056       nsNPAPIPluginInstance *oldestInstance = FindOldestStoppedInstance();
  3057       if (oldestInstance) {
  3058         nsPluginTag* pluginTag = TagForPlugin(oldestInstance->GetPlugin());
  3059         oldestInstance->Destroy();
  3060         mInstances.RemoveElement(oldestInstance);
  3061         // TODO: Remove this check once bug 752422 was investigated
  3062         if (pluginTag) {          
  3063           OnPluginInstanceDestroyed(pluginTag);
  3067   } else {
  3068     nsPluginTag* pluginTag = TagForPlugin(aInstance->GetPlugin());
  3069     aInstance->Destroy();
  3070     mInstances.RemoveElement(aInstance);
  3071     // TODO: Remove this check once bug 752422 was investigated
  3072     if (pluginTag) {      
  3073       OnPluginInstanceDestroyed(pluginTag);
  3077   return NS_OK;
  3080 nsresult nsPluginHost::NewPluginStreamListener(nsIURI* aURI,
  3081                                                nsNPAPIPluginInstance* aInstance,
  3082                                                nsIStreamListener **aStreamListener)
  3084   NS_ENSURE_ARG_POINTER(aURI);
  3085   NS_ENSURE_ARG_POINTER(aStreamListener);
  3087   nsRefPtr<nsPluginStreamListenerPeer> listener = new nsPluginStreamListenerPeer();
  3088   nsresult rv = listener->Initialize(aURI, aInstance, nullptr);
  3089   if (NS_FAILED(rv)) {
  3090     return rv;
  3093   listener.forget(aStreamListener);
  3095   return NS_OK;
  3098 NS_IMETHODIMP nsPluginHost::Observe(nsISupports *aSubject,
  3099                                     const char *aTopic,
  3100                                     const char16_t *someData)
  3102   if (!strcmp(NS_XPCOM_SHUTDOWN_OBSERVER_ID, aTopic)) {
  3103     OnShutdown();
  3104     UnloadPlugins();
  3105     sInst->Release();
  3107   if (!strcmp(NS_PREFBRANCH_PREFCHANGE_TOPIC_ID, aTopic)) {
  3108     mPluginsDisabled = Preferences::GetBool("plugin.disable", false);
  3109     mPluginsClickToPlay = Preferences::GetBool("plugins.click_to_play", false);
  3110     // Unload or load plugins as needed
  3111     if (mPluginsDisabled) {
  3112       UnloadPlugins();
  3113     } else {
  3114       LoadPlugins();
  3117   if (!strcmp("blocklist-updated", aTopic)) {
  3118     nsPluginTag* plugin = mPlugins;
  3119     while (plugin) {
  3120       plugin->InvalidateBlocklistState();
  3121       plugin = plugin->mNext;
  3124 #ifdef MOZ_WIDGET_ANDROID
  3125   if (!strcmp("application-background", aTopic)) {
  3126     for(uint32_t i = 0; i < mInstances.Length(); i++) {
  3127       mInstances[i]->NotifyForeground(false);
  3130   if (!strcmp("application-foreground", aTopic)) {
  3131     for(uint32_t i = 0; i < mInstances.Length(); i++) {
  3132       if (mInstances[i]->IsOnScreen())
  3133         mInstances[i]->NotifyForeground(true);
  3136   if (!strcmp("memory-pressure", aTopic)) {
  3137     for(uint32_t i = 0; i < mInstances.Length(); i++) {
  3138       mInstances[i]->MemoryPressure();
  3141 #endif
  3142   return NS_OK;
  3145 nsresult
  3146 nsPluginHost::ParsePostBufferToFixHeaders(const char *inPostData, uint32_t inPostDataLen,
  3147                                           char **outPostData, uint32_t *outPostDataLen)
  3149   if (!inPostData || !outPostData || !outPostDataLen)
  3150     return NS_ERROR_NULL_POINTER;
  3152   *outPostData = 0;
  3153   *outPostDataLen = 0;
  3155   const char CR = '\r';
  3156   const char LF = '\n';
  3157   const char CRLFCRLF[] = {CR,LF,CR,LF,'\0'}; // C string"\r\n\r\n"
  3158   const char ContentLenHeader[] = "Content-length";
  3160   nsAutoTArray<const char*, 8> singleLF;
  3161   const char *pSCntlh = 0;// pointer to start of ContentLenHeader in inPostData
  3162   const char *pSod = 0;   // pointer to start of data in inPostData
  3163   const char *pEoh = 0;   // pointer to end of headers in inPostData
  3164   const char *pEod = inPostData + inPostDataLen; // pointer to end of inPostData
  3165   if (*inPostData == LF) {
  3166     // If no custom headers are required, simply add a blank
  3167     // line ('\n') to the beginning of the file or buffer.
  3168     // so *inPostData == '\n' is valid
  3169     pSod = inPostData + 1;
  3170   } else {
  3171     const char *s = inPostData; //tmp pointer to sourse inPostData
  3172     while (s < pEod) {
  3173       if (!pSCntlh &&
  3174           (*s == 'C' || *s == 'c') &&
  3175           (s + sizeof(ContentLenHeader) - 1 < pEod) &&
  3176           (!PL_strncasecmp(s, ContentLenHeader, sizeof(ContentLenHeader) - 1)))
  3178         // lets assume this is ContentLenHeader for now
  3179         const char *p = pSCntlh = s;
  3180         p += sizeof(ContentLenHeader) - 1;
  3181         // search for first CR or LF == end of ContentLenHeader
  3182         for (; p < pEod; p++) {
  3183           if (*p == CR || *p == LF) {
  3184             // got delimiter,
  3185             // one more check; if previous char is a digit
  3186             // most likely pSCntlh points to the start of ContentLenHeader
  3187             if (*(p-1) >= '0' && *(p-1) <= '9') {
  3188               s = p;
  3190             break; //for loop
  3193         if (pSCntlh == s) { // curret ptr is the same
  3194           pSCntlh = 0; // that was not ContentLenHeader
  3195           break; // there is nothing to parse, break *WHILE LOOP* here
  3199       if (*s == CR) {
  3200         if (pSCntlh && // only if ContentLenHeader is found we are looking for end of headers
  3201             ((s + sizeof(CRLFCRLF)-1) <= pEod) &&
  3202             !memcmp(s, CRLFCRLF, sizeof(CRLFCRLF)-1))
  3204           s += sizeof(CRLFCRLF)-1;
  3205           pEoh = pSod = s; // data stars here
  3206           break;
  3208       } else if (*s == LF) {
  3209         if (*(s-1) != CR) {
  3210           singleLF.AppendElement(s);
  3212         if (pSCntlh && (s+1 < pEod) && (*(s+1) == LF)) {
  3213           s++;
  3214           singleLF.AppendElement(s);
  3215           s++;
  3216           pEoh = pSod = s; // data stars here
  3217           break;
  3220       s++;
  3224   // deal with output buffer
  3225   if (!pSod) { // lets assume whole buffer is a data
  3226     pSod = inPostData;
  3229   uint32_t newBufferLen = 0;
  3230   uint32_t dataLen = pEod - pSod;
  3231   uint32_t headersLen = pEoh ? pSod - inPostData : 0;
  3233   char *p; // tmp ptr into new output buf
  3234   if (headersLen) { // we got a headers
  3235     // this function does not make any assumption on correctness
  3236     // of ContentLenHeader value in this case.
  3238     newBufferLen = dataLen + headersLen;
  3239     // in case there were single LFs in headers
  3240     // reserve an extra space for CR will be added before each single LF
  3241     int cntSingleLF = singleLF.Length();
  3242     newBufferLen += cntSingleLF;
  3244     if (!(*outPostData = p = (char*)nsMemory::Alloc(newBufferLen)))
  3245       return NS_ERROR_OUT_OF_MEMORY;
  3247     // deal with single LF
  3248     const char *s = inPostData;
  3249     if (cntSingleLF) {
  3250       for (int i=0; i<cntSingleLF; i++) {
  3251         const char *plf = singleLF.ElementAt(i); // ptr to single LF in headers
  3252         int n = plf - s; // bytes to copy
  3253         if (n) { // for '\n\n' there is nothing to memcpy
  3254           memcpy(p, s, n);
  3255           p += n;
  3257         *p++ = CR;
  3258         s = plf;
  3259         *p++ = *s++;
  3262     // are we done with headers?
  3263     headersLen = pEoh - s;
  3264     if (headersLen) { // not yet
  3265       memcpy(p, s, headersLen); // copy the rest
  3266       p += headersLen;
  3268   } else  if (dataLen) { // no ContentLenHeader is found but there is a data
  3269     // make new output buffer big enough
  3270     // to keep ContentLenHeader+value followed by data
  3271     uint32_t l = sizeof(ContentLenHeader) + sizeof(CRLFCRLF) + 32;
  3272     newBufferLen = dataLen + l;
  3273     if (!(*outPostData = p = (char*)nsMemory::Alloc(newBufferLen)))
  3274       return NS_ERROR_OUT_OF_MEMORY;
  3275     headersLen = PR_snprintf(p, l,"%s: %ld%s", ContentLenHeader, dataLen, CRLFCRLF);
  3276     if (headersLen == l) { // if PR_snprintf has ate all extra space consider this as an error
  3277       nsMemory::Free(p);
  3278       *outPostData = 0;
  3279       return NS_ERROR_FAILURE;
  3281     p += headersLen;
  3282     newBufferLen = headersLen + dataLen;
  3284   // at this point we've done with headers.
  3285   // there is a possibility that input buffer has only headers info in it
  3286   // which already parsed and copied into output buffer.
  3287   // copy the data
  3288   if (dataLen) {
  3289     memcpy(p, pSod, dataLen);
  3292   *outPostDataLen = newBufferLen;
  3294   return NS_OK;
  3297 nsresult
  3298 nsPluginHost::CreateTempFileToPost(const char *aPostDataURL, nsIFile **aTmpFile)
  3300   nsresult rv;
  3301   int64_t fileSize;
  3302   nsAutoCString filename;
  3304   // stat file == get size & convert file:///c:/ to c: if needed
  3305   nsCOMPtr<nsIFile> inFile;
  3306   rv = NS_GetFileFromURLSpec(nsDependentCString(aPostDataURL),
  3307                              getter_AddRefs(inFile));
  3308   if (NS_FAILED(rv)) {
  3309     nsCOMPtr<nsIFile> localFile;
  3310     rv = NS_NewNativeLocalFile(nsDependentCString(aPostDataURL), false,
  3311                                getter_AddRefs(localFile));
  3312     if (NS_FAILED(rv)) return rv;
  3313     inFile = localFile;
  3315   rv = inFile->GetFileSize(&fileSize);
  3316   if (NS_FAILED(rv)) return rv;
  3317   rv = inFile->GetNativePath(filename);
  3318   if (NS_FAILED(rv)) return rv;
  3320   if (fileSize != 0) {
  3321     nsCOMPtr<nsIInputStream> inStream;
  3322     rv = NS_NewLocalFileInputStream(getter_AddRefs(inStream), inFile);
  3323     if (NS_FAILED(rv)) return rv;
  3325     // Create a temporary file to write the http Content-length:
  3326     // %ld\r\n\" header and "\r\n" == end of headers for post data to
  3328     nsCOMPtr<nsIFile> tempFile;
  3329     rv = GetPluginTempDir(getter_AddRefs(tempFile));
  3330     if (NS_FAILED(rv))
  3331       return rv;
  3333     nsAutoCString inFileName;
  3334     inFile->GetNativeLeafName(inFileName);
  3335     // XXX hack around bug 70083
  3336     inFileName.Insert(NS_LITERAL_CSTRING("post-"), 0);
  3337     rv = tempFile->AppendNative(inFileName);
  3339     if (NS_FAILED(rv))
  3340       return rv;
  3342     // make it unique, and mode == 0600, not world-readable
  3343     rv = tempFile->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0600);
  3344     if (NS_FAILED(rv))
  3345       return rv;
  3347     nsCOMPtr<nsIOutputStream> outStream;
  3348     if (NS_SUCCEEDED(rv)) {
  3349       rv = NS_NewLocalFileOutputStream(getter_AddRefs(outStream),
  3350         tempFile,
  3351         (PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE),
  3352         0600); // 600 so others can't read our form data
  3354     NS_ASSERTION(NS_SUCCEEDED(rv), "Post data file couldn't be created!");
  3355     if (NS_FAILED(rv))
  3356       return rv;
  3358     char buf[1024];
  3359     uint32_t br, bw;
  3360     bool firstRead = true;
  3361     while (1) {
  3362       // Read() mallocs if buffer is null
  3363       rv = inStream->Read(buf, 1024, &br);
  3364       if (NS_FAILED(rv) || (int32_t)br <= 0)
  3365         break;
  3366       if (firstRead) {
  3367         //"For protocols in which the headers must be distinguished from the body,
  3368         // such as HTTP, the buffer or file should contain the headers, followed by
  3369         // a blank line, then the body. If no custom headers are required, simply
  3370         // add a blank line ('\n') to the beginning of the file or buffer.
  3372         char *parsedBuf;
  3373         // assuming first 1K (or what we got) has all headers in,
  3374         // lets parse it through nsPluginHost::ParsePostBufferToFixHeaders()
  3375         ParsePostBufferToFixHeaders((const char *)buf, br, &parsedBuf, &bw);
  3376         rv = outStream->Write(parsedBuf, bw, &br);
  3377         nsMemory::Free(parsedBuf);
  3378         if (NS_FAILED(rv) || (bw != br))
  3379           break;
  3381         firstRead = false;
  3382         continue;
  3384       bw = br;
  3385       rv = outStream->Write(buf, bw, &br);
  3386       if (NS_FAILED(rv) || (bw != br))
  3387         break;
  3390     inStream->Close();
  3391     outStream->Close();
  3392     if (NS_SUCCEEDED(rv))
  3393       tempFile.forget(aTmpFile);
  3395   return rv;
  3398 nsresult
  3399 nsPluginHost::NewPluginNativeWindow(nsPluginNativeWindow ** aPluginNativeWindow)
  3401   return PLUG_NewPluginNativeWindow(aPluginNativeWindow);
  3404 nsresult
  3405 nsPluginHost::GetPluginName(nsNPAPIPluginInstance *aPluginInstance,
  3406                             const char** aPluginName)
  3408   nsNPAPIPluginInstance *instance = static_cast<nsNPAPIPluginInstance*>(aPluginInstance);
  3409   if (!instance)
  3410     return NS_ERROR_FAILURE;
  3412   nsNPAPIPlugin* plugin = instance->GetPlugin();
  3413   if (!plugin)
  3414     return NS_ERROR_FAILURE;
  3416   *aPluginName = TagForPlugin(plugin)->mName.get();
  3418   return NS_OK;
  3421 nsresult
  3422 nsPluginHost::GetPluginTagForInstance(nsNPAPIPluginInstance *aPluginInstance,
  3423                                       nsIPluginTag **aPluginTag)
  3425   NS_ENSURE_ARG_POINTER(aPluginInstance);
  3426   NS_ENSURE_ARG_POINTER(aPluginTag);
  3428   nsNPAPIPlugin *plugin = aPluginInstance->GetPlugin();
  3429   if (!plugin)
  3430     return NS_ERROR_FAILURE;
  3432   *aPluginTag = TagForPlugin(plugin);
  3434   NS_ADDREF(*aPluginTag);
  3435   return NS_OK;
  3438 NS_IMETHODIMP nsPluginHost::Notify(nsITimer* timer)
  3440   nsRefPtr<nsPluginTag> pluginTag = mPlugins;
  3441   while (pluginTag) {
  3442     if (pluginTag->mUnloadTimer == timer) {
  3443       if (!IsRunningPlugin(pluginTag)) {
  3444         pluginTag->TryUnloadPlugin(false);
  3446       return NS_OK;
  3448     pluginTag = pluginTag->mNext;
  3451   return NS_ERROR_FAILURE;
  3454 #ifdef XP_WIN
  3455 // Re-enable any top level browser windows that were disabled by modal dialogs
  3456 // displayed by the crashed plugin.
  3457 static void
  3458 CheckForDisabledWindows()
  3460   nsCOMPtr<nsIWindowMediator> wm(do_GetService(NS_WINDOWMEDIATOR_CONTRACTID));
  3461   if (!wm)
  3462     return;
  3464   nsCOMPtr<nsISimpleEnumerator> windowList;
  3465   wm->GetXULWindowEnumerator(nullptr, getter_AddRefs(windowList));
  3466   if (!windowList)
  3467     return;
  3469   bool haveWindows;
  3470   do {
  3471     windowList->HasMoreElements(&haveWindows);
  3472     if (!haveWindows)
  3473       return;
  3475     nsCOMPtr<nsISupports> supportsWindow;
  3476     windowList->GetNext(getter_AddRefs(supportsWindow));
  3477     nsCOMPtr<nsIBaseWindow> baseWin(do_QueryInterface(supportsWindow));
  3478     if (baseWin) {
  3479       nsCOMPtr<nsIWidget> widget;
  3480       baseWin->GetMainWidget(getter_AddRefs(widget));
  3481       if (widget && !widget->GetParent() &&
  3482           widget->IsVisible() &&
  3483           !widget->IsEnabled()) {
  3484         nsIWidget* child = widget->GetFirstChild();
  3485         bool enable = true;
  3486         while (child)  {
  3487           if (child->WindowType() == eWindowType_dialog) {
  3488             enable = false;
  3489             break;
  3491           child = child->GetNextSibling();
  3493         if (enable) {
  3494           widget->Enable(true);
  3498   } while (haveWindows);
  3500 #endif
  3502 void
  3503 nsPluginHost::PluginCrashed(nsNPAPIPlugin* aPlugin,
  3504                             const nsAString& pluginDumpID,
  3505                             const nsAString& browserDumpID)
  3507   nsPluginTag* crashedPluginTag = TagForPlugin(aPlugin);
  3509   // Notify the app's observer that a plugin crashed so it can submit
  3510   // a crashreport.
  3511   bool submittedCrashReport = false;
  3512   nsCOMPtr<nsIObserverService> obsService =
  3513     mozilla::services::GetObserverService();
  3514   nsCOMPtr<nsIWritablePropertyBag2> propbag =
  3515     do_CreateInstance("@mozilla.org/hash-property-bag;1");
  3516   if (obsService && propbag) {
  3517     propbag->SetPropertyAsAString(NS_LITERAL_STRING("pluginDumpID"),
  3518                                   pluginDumpID);
  3519     propbag->SetPropertyAsAString(NS_LITERAL_STRING("browserDumpID"),
  3520                                   browserDumpID);
  3521     propbag->SetPropertyAsBool(NS_LITERAL_STRING("submittedCrashReport"),
  3522                                submittedCrashReport);
  3523     obsService->NotifyObservers(propbag, "plugin-crashed", nullptr);
  3524     // see if an observer submitted a crash report.
  3525     propbag->GetPropertyAsBool(NS_LITERAL_STRING("submittedCrashReport"),
  3526                                &submittedCrashReport);
  3529   // Invalidate each nsPluginInstanceTag for the crashed plugin
  3531   for (uint32_t i = mInstances.Length(); i > 0; i--) {
  3532     nsNPAPIPluginInstance* instance = mInstances[i - 1];
  3533     if (instance->GetPlugin() == aPlugin) {
  3534       // notify the content node (nsIObjectLoadingContent) that the
  3535       // plugin has crashed
  3536       nsCOMPtr<nsIDOMElement> domElement;
  3537       instance->GetDOMElement(getter_AddRefs(domElement));
  3538       nsCOMPtr<nsIObjectLoadingContent> objectContent(do_QueryInterface(domElement));
  3539       if (objectContent) {
  3540         objectContent->PluginCrashed(crashedPluginTag, pluginDumpID, browserDumpID,
  3541                                      submittedCrashReport);
  3544       instance->Destroy();
  3545       mInstances.RemoveElement(instance);
  3546       OnPluginInstanceDestroyed(crashedPluginTag);
  3550   // Only after all instances have been invalidated is it safe to null
  3551   // out nsPluginTag.mPlugin. The next time we try to create an
  3552   // instance of this plugin we reload it (launch a new plugin process).
  3554   crashedPluginTag->mPlugin = nullptr;
  3556 #ifdef XP_WIN
  3557   CheckForDisabledWindows();
  3558 #endif
  3561 nsNPAPIPluginInstance*
  3562 nsPluginHost::FindInstance(const char *mimetype)
  3564   for (uint32_t i = 0; i < mInstances.Length(); i++) {
  3565     nsNPAPIPluginInstance* instance = mInstances[i];
  3567     const char* mt;
  3568     nsresult rv = instance->GetMIMEType(&mt);
  3569     if (NS_FAILED(rv))
  3570       continue;
  3572     if (PL_strcasecmp(mt, mimetype) == 0)
  3573       return instance;
  3576   return nullptr;
  3579 nsNPAPIPluginInstance*
  3580 nsPluginHost::FindOldestStoppedInstance()
  3582   nsNPAPIPluginInstance *oldestInstance = nullptr;
  3583   TimeStamp oldestTime = TimeStamp::Now();
  3584   for (uint32_t i = 0; i < mInstances.Length(); i++) {
  3585     nsNPAPIPluginInstance *instance = mInstances[i];
  3586     if (instance->IsRunning())
  3587       continue;
  3589     TimeStamp time = instance->StopTime();
  3590     if (time < oldestTime) {
  3591       oldestTime = time;
  3592       oldestInstance = instance;
  3596   return oldestInstance;
  3599 uint32_t
  3600 nsPluginHost::StoppedInstanceCount()
  3602   uint32_t stoppedCount = 0;
  3603   for (uint32_t i = 0; i < mInstances.Length(); i++) {
  3604     nsNPAPIPluginInstance *instance = mInstances[i];
  3605     if (!instance->IsRunning())
  3606       stoppedCount++;
  3608   return stoppedCount;
  3611 nsTArray< nsRefPtr<nsNPAPIPluginInstance> >*
  3612 nsPluginHost::InstanceArray()
  3614   return &mInstances;
  3617 void 
  3618 nsPluginHost::DestroyRunningInstances(nsPluginTag* aPluginTag)
  3620   for (int32_t i = mInstances.Length(); i > 0; i--) {
  3621     nsNPAPIPluginInstance *instance = mInstances[i - 1];
  3622     if (instance->IsRunning() && (!aPluginTag || aPluginTag == TagForPlugin(instance->GetPlugin()))) {
  3623       instance->SetWindow(nullptr);
  3624       instance->Stop();
  3626       // Get rid of all the instances without the possibility of caching.
  3627       nsPluginTag* pluginTag = TagForPlugin(instance->GetPlugin());
  3628       instance->SetWindow(nullptr);
  3630       nsCOMPtr<nsIDOMElement> domElement;
  3631       instance->GetDOMElement(getter_AddRefs(domElement));
  3632       nsCOMPtr<nsIObjectLoadingContent> objectContent =
  3633         do_QueryInterface(domElement);
  3635       instance->Destroy();
  3637       mInstances.RemoveElement(instance);
  3638       OnPluginInstanceDestroyed(pluginTag);
  3640       // Notify owning content that we destroyed its plugin out from under it
  3641       if (objectContent) {
  3642         objectContent->PluginDestroyed();
  3648 // Runnable that does an async destroy of a plugin.
  3650 class nsPluginDestroyRunnable : public nsRunnable,
  3651                                 public PRCList
  3653 public:
  3654   nsPluginDestroyRunnable(nsNPAPIPluginInstance *aInstance)
  3655     : mInstance(aInstance)
  3657     PR_INIT_CLIST(this);
  3658     PR_APPEND_LINK(this, &sRunnableListHead);
  3661   virtual ~nsPluginDestroyRunnable()
  3663     PR_REMOVE_LINK(this);
  3666   NS_IMETHOD Run()
  3668     nsRefPtr<nsNPAPIPluginInstance> instance;
  3670     // Null out mInstance to make sure this code in another runnable
  3671     // will do the right thing even if someone was holding on to this
  3672     // runnable longer than we expect.
  3673     instance.swap(mInstance);
  3675     if (PluginDestructionGuard::DelayDestroy(instance)) {
  3676       // It's still not safe to destroy the plugin, it's now up to the
  3677       // outermost guard on the stack to take care of the destruction.
  3678       return NS_OK;
  3681     nsPluginDestroyRunnable *r =
  3682       static_cast<nsPluginDestroyRunnable*>(PR_NEXT_LINK(&sRunnableListHead));
  3684     while (r != &sRunnableListHead) {
  3685       if (r != this && r->mInstance == instance) {
  3686         // There's another runnable scheduled to tear down
  3687         // instance. Let it do the job.
  3688         return NS_OK;
  3690       r = static_cast<nsPluginDestroyRunnable*>(PR_NEXT_LINK(r));
  3693     PLUGIN_LOG(PLUGIN_LOG_NORMAL,
  3694                ("Doing delayed destroy of instance %p\n", instance.get()));
  3696     nsRefPtr<nsPluginHost> host = nsPluginHost::GetInst();
  3697     if (host)
  3698       host->StopPluginInstance(instance);
  3700     PLUGIN_LOG(PLUGIN_LOG_NORMAL,
  3701                ("Done with delayed destroy of instance %p\n", instance.get()));
  3703     return NS_OK;
  3706 protected:
  3707   nsRefPtr<nsNPAPIPluginInstance> mInstance;
  3709   static PRCList sRunnableListHead;
  3710 };
  3712 PRCList nsPluginDestroyRunnable::sRunnableListHead =
  3713   PR_INIT_STATIC_CLIST(&nsPluginDestroyRunnable::sRunnableListHead);
  3715 PRCList PluginDestructionGuard::sListHead =
  3716   PR_INIT_STATIC_CLIST(&PluginDestructionGuard::sListHead);
  3718 PluginDestructionGuard::PluginDestructionGuard(nsNPAPIPluginInstance *aInstance)
  3719   : mInstance(aInstance)
  3721   Init();
  3724 PluginDestructionGuard::PluginDestructionGuard(NPP npp)
  3725   : mInstance(npp ? static_cast<nsNPAPIPluginInstance*>(npp->ndata) : nullptr)
  3727   Init();
  3730 PluginDestructionGuard::~PluginDestructionGuard()
  3732   NS_ASSERTION(NS_IsMainThread(), "Should be on the main thread");
  3734   PR_REMOVE_LINK(this);
  3736   if (mDelayedDestroy) {
  3737     // We've attempted to destroy the plugin instance we're holding on
  3738     // to while we were guarding it. Do the actual destroy now, off of
  3739     // a runnable.
  3740     nsRefPtr<nsPluginDestroyRunnable> evt =
  3741       new nsPluginDestroyRunnable(mInstance);
  3743     NS_DispatchToMainThread(evt);
  3747 // static
  3748 bool
  3749 PluginDestructionGuard::DelayDestroy(nsNPAPIPluginInstance *aInstance)
  3751   NS_ASSERTION(NS_IsMainThread(), "Should be on the main thread");
  3752   NS_ASSERTION(aInstance, "Uh, I need an instance!");
  3754   // Find the first guard on the stack and make it do a delayed
  3755   // destroy upon destruction.
  3757   PluginDestructionGuard *g =
  3758     static_cast<PluginDestructionGuard*>(PR_LIST_HEAD(&sListHead));
  3760   while (g != &sListHead) {
  3761     if (g->mInstance == aInstance) {
  3762       g->mDelayedDestroy = true;
  3764       return true;
  3766     g = static_cast<PluginDestructionGuard*>(PR_NEXT_LINK(g));
  3769   return false;

mercurial