michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: /* nsPluginHost.cpp - top-level plugin management code */ michael@0: michael@0: #include "nscore.h" michael@0: #include "nsPluginHost.h" michael@0: michael@0: #include michael@0: #include michael@0: #include "prio.h" michael@0: #include "prmem.h" michael@0: #include "nsNPAPIPlugin.h" michael@0: #include "nsNPAPIPluginStreamListener.h" michael@0: #include "nsNPAPIPluginInstance.h" michael@0: #include "nsPluginInstanceOwner.h" michael@0: #include "nsObjectLoadingContent.h" michael@0: #include "nsIHTTPHeaderListener.h" michael@0: #include "nsIHttpHeaderVisitor.h" michael@0: #include "nsIObserverService.h" michael@0: #include "nsIHttpProtocolHandler.h" michael@0: #include "nsIHttpChannel.h" michael@0: #include "nsIUploadChannel.h" michael@0: #include "nsIByteRangeRequest.h" michael@0: #include "nsIStreamListener.h" michael@0: #include "nsIInputStream.h" michael@0: #include "nsIOutputStream.h" michael@0: #include "nsIURL.h" michael@0: #include "nsTArray.h" michael@0: #include "nsReadableUtils.h" michael@0: #include "nsProtocolProxyService.h" michael@0: #include "nsIStreamConverterService.h" michael@0: #include "nsIFile.h" michael@0: #if defined(XP_MACOSX) michael@0: #include "nsILocalFileMac.h" michael@0: #endif michael@0: #include "nsISeekableStream.h" michael@0: #include "nsNetUtil.h" michael@0: #include "nsIProgressEventSink.h" michael@0: #include "nsIDocument.h" michael@0: #include "nsPluginLogging.h" michael@0: #include "nsIScriptChannel.h" michael@0: #include "nsIBlocklistService.h" michael@0: #include "nsVersionComparator.h" michael@0: #include "nsIObjectLoadingContent.h" michael@0: #include "nsIWritablePropertyBag2.h" michael@0: #include "nsICategoryManager.h" michael@0: #include "nsPluginStreamListenerPeer.h" michael@0: #include "mozilla/Preferences.h" michael@0: michael@0: #include "nsEnumeratorUtils.h" michael@0: #include "nsXPCOM.h" michael@0: #include "nsXPCOMCID.h" michael@0: #include "nsISupportsPrimitives.h" michael@0: michael@0: #include "nsXULAppAPI.h" michael@0: #include "nsIXULRuntime.h" michael@0: michael@0: // for the dialog michael@0: #include "nsIWindowWatcher.h" michael@0: #include "nsIDOMElement.h" michael@0: #include "nsIDOMWindow.h" michael@0: michael@0: #include "nsNetCID.h" michael@0: #include "prprf.h" michael@0: #include "nsThreadUtils.h" michael@0: #include "nsIInputStreamTee.h" michael@0: michael@0: #include "nsDirectoryServiceDefs.h" michael@0: #include "nsAppDirectoryServiceDefs.h" michael@0: #include "nsPluginDirServiceProvider.h" michael@0: michael@0: #include "nsUnicharUtils.h" michael@0: #include "nsPluginManifestLineReader.h" michael@0: michael@0: #include "nsIWeakReferenceUtils.h" michael@0: #include "nsIPresShell.h" michael@0: #include "nsPluginNativeWindow.h" michael@0: #include "nsIScriptSecurityManager.h" michael@0: #include "nsIContentPolicy.h" michael@0: #include "nsContentPolicyUtils.h" michael@0: #include "mozilla/TimeStamp.h" michael@0: #include "mozilla/Telemetry.h" michael@0: #include "nsIImageLoadingContent.h" michael@0: #include "mozilla/Preferences.h" michael@0: #include "nsVersionComparator.h" michael@0: michael@0: #if defined(XP_WIN) michael@0: #include "nsIWindowMediator.h" michael@0: #include "nsIBaseWindow.h" michael@0: #include "windows.h" michael@0: #include "winbase.h" michael@0: #endif michael@0: michael@0: #ifdef ANDROID michael@0: #include michael@0: #define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "GeckoPlugins" , ## args) michael@0: #endif michael@0: michael@0: #if MOZ_CRASHREPORTER michael@0: #include "nsExceptionHandler.h" michael@0: #endif michael@0: michael@0: using namespace mozilla; michael@0: using mozilla::TimeStamp; michael@0: michael@0: // Null out a strong ref to a linked list iteratively to avoid michael@0: // exhausting the stack (bug 486349). michael@0: #define NS_ITERATIVE_UNREF_LIST(type_, list_, mNext_) \ michael@0: { \ michael@0: while (list_) { \ michael@0: type_ temp = list_->mNext_; \ michael@0: list_->mNext_ = nullptr; \ michael@0: list_ = temp; \ michael@0: } \ michael@0: } michael@0: michael@0: // this is the name of the directory which will be created michael@0: // to cache temporary files. michael@0: #define kPluginTmpDirName NS_LITERAL_CSTRING("plugtmp") michael@0: michael@0: static const char *kPrefWhitelist = "plugin.allowed_types"; michael@0: static const char *kPrefDisableFullPage = "plugin.disable_full_page_plugin_for_types"; michael@0: static const char *kPrefJavaMIME = "plugin.java.mime"; michael@0: michael@0: // Version of cached plugin info michael@0: // 0.01 first implementation michael@0: // 0.02 added caching of CanUnload to fix bug 105935 michael@0: // 0.03 changed name, description and mime desc from string to bytes, bug 108246 michael@0: // 0.04 added new mime entry point on Mac, bug 113464 michael@0: // 0.05 added new entry point check for the default plugin, bug 132430 michael@0: // 0.06 strip off suffixes in mime description strings, bug 53895 michael@0: // 0.07 changed nsIRegistry to flat file support for caching plugins info michael@0: // 0.08 mime entry point on MachO, bug 137535 michael@0: // 0.09 the file encoding is changed to UTF-8, bug 420285 michael@0: // 0.10 added plugin versions on appropriate platforms, bug 427743 michael@0: // 0.11 file name and full path fields now store expected values on all platforms, bug 488181 michael@0: // 0.12 force refresh due to quicktime pdf claim fix, bug 611197 michael@0: // 0.13 add architecture and list of invalid plugins, bug 616271 michael@0: // 0.14 force refresh due to locale comparison fix, bug 611296 michael@0: // 0.15 force refresh due to bug in reading Java plist MIME data, bug 638171 michael@0: // 0.16 version bump to avoid importing the plugin flags in newer versions michael@0: // 0.17 added flag on whether plugin is loaded from an XPI michael@0: // The current plugin registry version (and the maximum version we know how to read) michael@0: static const char *kPluginRegistryVersion = "0.17"; michael@0: // The minimum registry version we know how to read michael@0: static const char *kMinimumRegistryVersion = "0.9"; michael@0: michael@0: static const char kDirectoryServiceContractID[] = "@mozilla.org/file/directory_service;1"; michael@0: michael@0: #define kPluginRegistryFilename NS_LITERAL_CSTRING("pluginreg.dat") michael@0: michael@0: #ifdef PLUGIN_LOGGING michael@0: PRLogModuleInfo* nsPluginLogging::gNPNLog = nullptr; michael@0: PRLogModuleInfo* nsPluginLogging::gNPPLog = nullptr; michael@0: PRLogModuleInfo* nsPluginLogging::gPluginLog = nullptr; michael@0: #endif michael@0: michael@0: // #defines for plugin cache and prefs michael@0: #define NS_PREF_MAX_NUM_CACHED_INSTANCES "browser.plugins.max_num_cached_plugins" michael@0: // Raise this from '10' to '50' to work around a bug in Apple's current Java michael@0: // plugins on OS X Lion and SnowLeopard. See bug 705931. michael@0: #define DEFAULT_NUMBER_OF_STOPPED_INSTANCES 50 michael@0: michael@0: nsIFile *nsPluginHost::sPluginTempDir; michael@0: nsPluginHost *nsPluginHost::sInst; michael@0: michael@0: NS_IMPL_ISUPPORTS0(nsInvalidPluginTag) michael@0: michael@0: nsInvalidPluginTag::nsInvalidPluginTag(const char* aFullPath, int64_t aLastModifiedTime) michael@0: : mFullPath(aFullPath), michael@0: mLastModifiedTime(aLastModifiedTime), michael@0: mSeen(false) michael@0: {} michael@0: michael@0: nsInvalidPluginTag::~nsInvalidPluginTag() michael@0: {} michael@0: michael@0: // Helper to check for a MIME in a comma-delimited preference michael@0: static bool michael@0: IsTypeInList(nsCString &aMimeType, nsCString aTypeList) michael@0: { michael@0: nsAutoCString searchStr; michael@0: searchStr.Assign(','); michael@0: searchStr.Append(aTypeList); michael@0: searchStr.Append(','); michael@0: michael@0: nsACString::const_iterator start, end; michael@0: michael@0: searchStr.BeginReading(start); michael@0: searchStr.EndReading(end); michael@0: michael@0: nsAutoCString commaSeparated; michael@0: commaSeparated.Assign(','); michael@0: commaSeparated += aMimeType; michael@0: commaSeparated.Append(','); michael@0: michael@0: return FindInReadable(commaSeparated, start, end); michael@0: } michael@0: michael@0: // flat file reg funcs michael@0: static michael@0: bool ReadSectionHeader(nsPluginManifestLineReader& reader, const char *token) michael@0: { michael@0: do { michael@0: if (*reader.LinePtr() == '[') { michael@0: char* p = reader.LinePtr() + (reader.LineLength() - 1); michael@0: if (*p != ']') michael@0: break; michael@0: *p = 0; michael@0: michael@0: char* values[1]; michael@0: if (1 != reader.ParseLine(values, 1)) michael@0: break; michael@0: // ignore the leading '[' michael@0: if (PL_strcmp(values[0]+1, token)) { michael@0: break; // it's wrong token michael@0: } michael@0: return true; michael@0: } michael@0: } while (reader.NextLine()); michael@0: return false; michael@0: } michael@0: michael@0: static bool UnloadPluginsASAP() michael@0: { michael@0: return Preferences::GetBool("dom.ipc.plugins.unloadASAP", false); michael@0: } michael@0: michael@0: nsPluginHost::nsPluginHost() michael@0: // No need to initialize members to nullptr, false etc because this class michael@0: // has a zeroing operator new. michael@0: { michael@0: // check to see if pref is set at startup to let plugins take over in michael@0: // full page mode for certain image mime types that we handle internally michael@0: mOverrideInternalTypes = michael@0: Preferences::GetBool("plugin.override_internal_types", false); michael@0: michael@0: mPluginsDisabled = Preferences::GetBool("plugin.disable", false); michael@0: mPluginsClickToPlay = Preferences::GetBool("plugins.click_to_play", false); michael@0: michael@0: Preferences::AddStrongObserver(this, "plugin.disable"); michael@0: Preferences::AddStrongObserver(this, "plugins.click_to_play"); michael@0: michael@0: nsCOMPtr obsService = michael@0: mozilla::services::GetObserverService(); michael@0: if (obsService) { michael@0: obsService->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false); michael@0: obsService->AddObserver(this, "blocklist-updated", false); michael@0: #ifdef MOZ_WIDGET_ANDROID michael@0: obsService->AddObserver(this, "application-foreground", false); michael@0: obsService->AddObserver(this, "application-background", false); michael@0: #endif michael@0: } michael@0: michael@0: #ifdef PLUGIN_LOGGING michael@0: nsPluginLogging::gNPNLog = PR_NewLogModule(NPN_LOG_NAME); michael@0: nsPluginLogging::gNPPLog = PR_NewLogModule(NPP_LOG_NAME); michael@0: nsPluginLogging::gPluginLog = PR_NewLogModule(PLUGIN_LOG_NAME); michael@0: michael@0: PR_LOG(nsPluginLogging::gNPNLog, PLUGIN_LOG_ALWAYS,("NPN Logging Active!\n")); michael@0: PR_LOG(nsPluginLogging::gPluginLog, PLUGIN_LOG_ALWAYS,("General Plugin Logging Active! (nsPluginHost::ctor)\n")); michael@0: PR_LOG(nsPluginLogging::gNPPLog, PLUGIN_LOG_ALWAYS,("NPP Logging Active!\n")); michael@0: michael@0: PLUGIN_LOG(PLUGIN_LOG_ALWAYS,("nsPluginHost::ctor\n")); michael@0: PR_LogFlush(); michael@0: #endif michael@0: } michael@0: michael@0: nsPluginHost::~nsPluginHost() michael@0: { michael@0: PLUGIN_LOG(PLUGIN_LOG_ALWAYS,("nsPluginHost::dtor\n")); michael@0: michael@0: UnloadPlugins(); michael@0: sInst = nullptr; michael@0: } michael@0: michael@0: NS_IMPL_ISUPPORTS(nsPluginHost, michael@0: nsIPluginHost, michael@0: nsIObserver, michael@0: nsITimerCallback, michael@0: nsISupportsWeakReference) michael@0: michael@0: already_AddRefed michael@0: nsPluginHost::GetInst() michael@0: { michael@0: if (!sInst) { michael@0: sInst = new nsPluginHost(); michael@0: if (!sInst) michael@0: return nullptr; michael@0: NS_ADDREF(sInst); michael@0: } michael@0: michael@0: nsRefPtr inst = sInst; michael@0: return inst.forget(); michael@0: } michael@0: michael@0: bool nsPluginHost::IsRunningPlugin(nsPluginTag * aPluginTag) michael@0: { michael@0: if (!aPluginTag || !aPluginTag->mPlugin) { michael@0: return false; michael@0: } michael@0: michael@0: for (uint32_t i = 0; i < mInstances.Length(); i++) { michael@0: nsNPAPIPluginInstance *instance = mInstances[i].get(); michael@0: if (instance && michael@0: instance->GetPlugin() == aPluginTag->mPlugin && michael@0: instance->IsRunning()) { michael@0: return true; michael@0: } michael@0: } michael@0: michael@0: return false; michael@0: } michael@0: michael@0: nsresult nsPluginHost::ReloadPlugins() michael@0: { michael@0: PLUGIN_LOG(PLUGIN_LOG_NORMAL, michael@0: ("nsPluginHost::ReloadPlugins Begin\n")); michael@0: michael@0: nsresult rv = NS_OK; michael@0: michael@0: // this will create the initial plugin list out of cache michael@0: // if it was not created yet michael@0: if (!mPluginsLoaded) michael@0: return LoadPlugins(); michael@0: michael@0: // we are re-scanning plugins. New plugins may have been added, also some michael@0: // plugins may have been removed, so we should probably shut everything down michael@0: // but don't touch running (active and not stopped) plugins michael@0: michael@0: // check if plugins changed, no need to do anything else michael@0: // if no changes to plugins have been made michael@0: // false instructs not to touch the plugin list, just to michael@0: // look for possible changes michael@0: bool pluginschanged = true; michael@0: FindPlugins(false, &pluginschanged); michael@0: michael@0: // if no changed detected, return an appropriate error code michael@0: if (!pluginschanged) michael@0: return NS_ERROR_PLUGINS_PLUGINSNOTCHANGED; michael@0: michael@0: // shutdown plugins and kill the list if there are no running plugins michael@0: nsRefPtr prev; michael@0: nsRefPtr next; michael@0: michael@0: for (nsRefPtr p = mPlugins; p != nullptr;) { michael@0: next = p->mNext; michael@0: michael@0: // only remove our plugin from the list if it's not running. michael@0: if (!IsRunningPlugin(p)) { michael@0: if (p == mPlugins) michael@0: mPlugins = next; michael@0: else michael@0: prev->mNext = next; michael@0: michael@0: p->mNext = nullptr; michael@0: michael@0: // attempt to unload plugins whenever they are removed from the list michael@0: p->TryUnloadPlugin(false); michael@0: michael@0: p = next; michael@0: continue; michael@0: } michael@0: michael@0: prev = p; michael@0: p = next; michael@0: } michael@0: michael@0: // set flags michael@0: mPluginsLoaded = false; michael@0: michael@0: // load them again michael@0: rv = LoadPlugins(); michael@0: michael@0: PLUGIN_LOG(PLUGIN_LOG_NORMAL, michael@0: ("nsPluginHost::ReloadPlugins End\n")); michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: #define NS_RETURN_UASTRING_SIZE 128 michael@0: michael@0: nsresult nsPluginHost::UserAgent(const char **retstring) michael@0: { michael@0: static char resultString[NS_RETURN_UASTRING_SIZE]; michael@0: nsresult res; michael@0: michael@0: nsCOMPtr http = do_GetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "http", &res); michael@0: if (NS_FAILED(res)) michael@0: return res; michael@0: michael@0: nsAutoCString uaString; michael@0: res = http->GetUserAgent(uaString); michael@0: michael@0: if (NS_SUCCEEDED(res)) { michael@0: if (NS_RETURN_UASTRING_SIZE > uaString.Length()) { michael@0: PL_strcpy(resultString, uaString.get()); michael@0: } else { michael@0: // Copy as much of UA string as we can (terminate at right-most space). michael@0: PL_strncpy(resultString, uaString.get(), NS_RETURN_UASTRING_SIZE); michael@0: for (int i = NS_RETURN_UASTRING_SIZE - 1; i >= 0; i--) { michael@0: if (i == 0) { michael@0: resultString[NS_RETURN_UASTRING_SIZE - 1] = '\0'; michael@0: } michael@0: else if (resultString[i] == ' ') { michael@0: resultString[i] = '\0'; michael@0: break; michael@0: } michael@0: } michael@0: } michael@0: *retstring = resultString; michael@0: } michael@0: else { michael@0: *retstring = nullptr; michael@0: } michael@0: michael@0: PLUGIN_LOG(PLUGIN_LOG_NORMAL, ("nsPluginHost::UserAgent return=%s\n", *retstring)); michael@0: michael@0: return res; michael@0: } michael@0: michael@0: nsresult nsPluginHost::GetURL(nsISupports* pluginInst, michael@0: const char* url, michael@0: const char* target, michael@0: nsNPAPIPluginStreamListener* streamListener, michael@0: const char* altHost, michael@0: const char* referrer, michael@0: bool forceJSEnabled) michael@0: { michael@0: return GetURLWithHeaders(static_cast(pluginInst), michael@0: url, target, streamListener, altHost, referrer, michael@0: forceJSEnabled, 0, nullptr); michael@0: } michael@0: michael@0: nsresult nsPluginHost::GetURLWithHeaders(nsNPAPIPluginInstance* pluginInst, michael@0: const char* url, michael@0: const char* target, michael@0: nsNPAPIPluginStreamListener* streamListener, michael@0: const char* altHost, michael@0: const char* referrer, michael@0: bool forceJSEnabled, michael@0: uint32_t getHeadersLength, michael@0: const char* getHeaders) michael@0: { michael@0: // we can only send a stream back to the plugin (as specified by a michael@0: // null target) if we also have a nsNPAPIPluginStreamListener to talk to michael@0: if (!target && !streamListener) michael@0: return NS_ERROR_ILLEGAL_VALUE; michael@0: michael@0: nsresult rv = DoURLLoadSecurityCheck(pluginInst, url); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: if (target) { michael@0: nsRefPtr owner = pluginInst->GetOwner(); michael@0: if (owner) { michael@0: if ((0 == PL_strcmp(target, "newwindow")) || michael@0: (0 == PL_strcmp(target, "_new"))) michael@0: target = "_blank"; michael@0: else if (0 == PL_strcmp(target, "_current")) michael@0: target = "_self"; michael@0: michael@0: rv = owner->GetURL(url, target, nullptr, nullptr, 0); michael@0: } michael@0: } michael@0: michael@0: if (streamListener) michael@0: rv = NewPluginURLStream(NS_ConvertUTF8toUTF16(url), pluginInst, michael@0: streamListener, nullptr, michael@0: getHeaders, getHeadersLength); michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: nsresult nsPluginHost::PostURL(nsISupports* pluginInst, michael@0: const char* url, michael@0: uint32_t postDataLen, michael@0: const char* postData, michael@0: bool isFile, michael@0: const char* target, michael@0: nsNPAPIPluginStreamListener* streamListener, michael@0: const char* altHost, michael@0: const char* referrer, michael@0: bool forceJSEnabled, michael@0: uint32_t postHeadersLength, michael@0: const char* postHeaders) michael@0: { michael@0: nsresult rv; michael@0: michael@0: // we can only send a stream back to the plugin (as specified michael@0: // by a null target) if we also have a nsNPAPIPluginStreamListener michael@0: // to talk to also michael@0: if (!target && !streamListener) michael@0: return NS_ERROR_ILLEGAL_VALUE; michael@0: michael@0: nsNPAPIPluginInstance* instance = static_cast(pluginInst); michael@0: michael@0: rv = DoURLLoadSecurityCheck(instance, url); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: nsCOMPtr postStream; michael@0: if (isFile) { michael@0: nsCOMPtr file; michael@0: rv = CreateTempFileToPost(postData, getter_AddRefs(file)); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: nsCOMPtr fileStream; michael@0: rv = NS_NewLocalFileInputStream(getter_AddRefs(fileStream), michael@0: file, michael@0: PR_RDONLY, michael@0: 0600, michael@0: nsIFileInputStream::DELETE_ON_CLOSE | michael@0: nsIFileInputStream::CLOSE_ON_EOF); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: rv = NS_NewBufferedInputStream(getter_AddRefs(postStream), fileStream, 8192); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: } else { michael@0: char *dataToPost; michael@0: uint32_t newDataToPostLen; michael@0: ParsePostBufferToFixHeaders(postData, postDataLen, &dataToPost, &newDataToPostLen); michael@0: if (!dataToPost) michael@0: return NS_ERROR_UNEXPECTED; michael@0: michael@0: nsCOMPtr sis = do_CreateInstance("@mozilla.org/io/string-input-stream;1", &rv); michael@0: if (!sis) { michael@0: NS_Free(dataToPost); michael@0: return rv; michael@0: } michael@0: michael@0: // data allocated by ParsePostBufferToFixHeaders() is managed and michael@0: // freed by the string stream. michael@0: postDataLen = newDataToPostLen; michael@0: sis->AdoptData(dataToPost, postDataLen); michael@0: postStream = sis; michael@0: } michael@0: michael@0: if (target) { michael@0: nsRefPtr owner = instance->GetOwner(); michael@0: if (owner) { michael@0: if ((0 == PL_strcmp(target, "newwindow")) || michael@0: (0 == PL_strcmp(target, "_new"))) { michael@0: target = "_blank"; michael@0: } else if (0 == PL_strcmp(target, "_current")) { michael@0: target = "_self"; michael@0: } michael@0: rv = owner->GetURL(url, target, postStream, michael@0: (void*)postHeaders, postHeadersLength); michael@0: } michael@0: } michael@0: michael@0: // if we don't have a target, just create a stream. This does michael@0: // NS_OpenURI()! michael@0: if (streamListener) michael@0: rv = NewPluginURLStream(NS_ConvertUTF8toUTF16(url), instance, michael@0: streamListener, michael@0: postStream, postHeaders, postHeadersLength); michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: /* This method queries the prefs for proxy information. michael@0: * It has been tested and is known to work in the following three cases michael@0: * when no proxy host or port is specified michael@0: * when only the proxy host is specified michael@0: * when only the proxy port is specified michael@0: * This method conforms to the return code specified in michael@0: * http://developer.netscape.com/docs/manuals/proxy/adminnt/autoconf.htm#1020923 michael@0: * with the exception that multiple values are not implemented. michael@0: */ michael@0: michael@0: nsresult nsPluginHost::FindProxyForURL(const char* url, char* *result) michael@0: { michael@0: if (!url || !result) { michael@0: return NS_ERROR_INVALID_ARG; michael@0: } michael@0: nsresult res; michael@0: michael@0: nsCOMPtr proxyService = michael@0: do_GetService(NS_PROTOCOLPROXYSERVICE_CONTRACTID, &res); michael@0: if (NS_FAILED(res) || !proxyService) michael@0: return res; michael@0: michael@0: nsRefPtr rawProxyService = do_QueryObject(proxyService); michael@0: if (!rawProxyService) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: nsCOMPtr ioService = do_GetService(NS_IOSERVICE_CONTRACTID, &res); michael@0: if (NS_FAILED(res) || !ioService) michael@0: return res; michael@0: michael@0: // make a temporary channel from the argument url michael@0: nsCOMPtr tempChannel; michael@0: res = ioService->NewChannel(nsDependentCString(url), nullptr, nullptr, getter_AddRefs(tempChannel)); michael@0: if (NS_FAILED(res)) michael@0: return res; michael@0: michael@0: nsCOMPtr pi; michael@0: michael@0: // Remove this deprecated call in the future (see Bug 778201): michael@0: res = rawProxyService->DeprecatedBlockingResolve(tempChannel, 0, getter_AddRefs(pi)); michael@0: if (NS_FAILED(res)) michael@0: return res; michael@0: michael@0: nsAutoCString host, type; michael@0: int32_t port = -1; michael@0: michael@0: // These won't fail, and even if they do... we'll be ok. michael@0: if (pi) { michael@0: pi->GetType(type); michael@0: pi->GetHost(host); michael@0: pi->GetPort(&port); michael@0: } michael@0: michael@0: if (!pi || host.IsEmpty() || port <= 0 || host.EqualsLiteral("direct")) { michael@0: *result = PL_strdup("DIRECT"); michael@0: } else if (type.EqualsLiteral("http")) { michael@0: *result = PR_smprintf("PROXY %s:%d", host.get(), port); michael@0: } else if (type.EqualsLiteral("socks4")) { michael@0: *result = PR_smprintf("SOCKS %s:%d", host.get(), port); michael@0: } else if (type.EqualsLiteral("socks")) { michael@0: // XXX - this is socks5, but there is no API for us to tell the michael@0: // plugin that fact. SOCKS for now, in case the proxy server michael@0: // speaks SOCKS4 as well. See bug 78176 michael@0: // For a long time this was returning an http proxy type, so michael@0: // very little is probably broken by this michael@0: *result = PR_smprintf("SOCKS %s:%d", host.get(), port); michael@0: } else { michael@0: NS_ASSERTION(false, "Unknown proxy type!"); michael@0: *result = PL_strdup("DIRECT"); michael@0: } michael@0: michael@0: if (nullptr == *result) michael@0: res = NS_ERROR_OUT_OF_MEMORY; michael@0: michael@0: return res; michael@0: } michael@0: michael@0: nsresult nsPluginHost::Init() michael@0: { michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult nsPluginHost::UnloadPlugins() michael@0: { michael@0: PLUGIN_LOG(PLUGIN_LOG_NORMAL, ("nsPluginHost::UnloadPlugins Called\n")); michael@0: michael@0: if (!mPluginsLoaded) michael@0: return NS_OK; michael@0: michael@0: // we should call nsIPluginInstance::Stop and nsIPluginInstance::SetWindow michael@0: // for those plugins who want it michael@0: DestroyRunningInstances(nullptr); michael@0: michael@0: nsPluginTag *pluginTag; michael@0: for (pluginTag = mPlugins; pluginTag; pluginTag = pluginTag->mNext) { michael@0: pluginTag->TryUnloadPlugin(true); michael@0: } michael@0: michael@0: NS_ITERATIVE_UNREF_LIST(nsRefPtr, mPlugins, mNext); michael@0: NS_ITERATIVE_UNREF_LIST(nsRefPtr, mCachedPlugins, mNext); michael@0: NS_ITERATIVE_UNREF_LIST(nsRefPtr, mInvalidPlugins, mNext); michael@0: michael@0: // Lets remove any of the temporary files that we created. michael@0: if (sPluginTempDir) { michael@0: sPluginTempDir->Remove(true); michael@0: NS_RELEASE(sPluginTempDir); michael@0: } michael@0: michael@0: #ifdef XP_WIN michael@0: if (mPrivateDirServiceProvider) { michael@0: nsCOMPtr dirService = michael@0: do_GetService(kDirectoryServiceContractID); michael@0: if (dirService) michael@0: dirService->UnregisterProvider(mPrivateDirServiceProvider); michael@0: mPrivateDirServiceProvider = nullptr; michael@0: } michael@0: #endif /* XP_WIN */ michael@0: michael@0: mPluginsLoaded = false; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: void nsPluginHost::OnPluginInstanceDestroyed(nsPluginTag* aPluginTag) michael@0: { michael@0: bool hasInstance = false; michael@0: for (uint32_t i = 0; i < mInstances.Length(); i++) { michael@0: if (TagForPlugin(mInstances[i]->GetPlugin()) == aPluginTag) { michael@0: hasInstance = true; michael@0: break; michael@0: } michael@0: } michael@0: michael@0: // We have some options for unloading plugins if they have no instances. michael@0: // michael@0: // Unloading plugins immediately can be bad - some plugins retain state michael@0: // between instances even when there are none. This is largely limited to michael@0: // going from one page to another, so state is retained without an instance michael@0: // for only a very short period of time. In order to allow this to work michael@0: // we don't unload plugins immediately by default. This is supported michael@0: // via a hidden user pref though. michael@0: // michael@0: // Another reason not to unload immediately is that loading is expensive, michael@0: // and it is better to leave popular plugins loaded. michael@0: // michael@0: // Our default behavior is to try to unload a plugin three minutes after michael@0: // its last instance is destroyed. This seems like a reasonable compromise michael@0: // that allows us to reclaim memory while allowing short state retention michael@0: // and avoid perf hits for loading popular plugins. michael@0: if (!hasInstance) { michael@0: if (UnloadPluginsASAP()) { michael@0: aPluginTag->TryUnloadPlugin(false); michael@0: } else { michael@0: if (aPluginTag->mUnloadTimer) { michael@0: aPluginTag->mUnloadTimer->Cancel(); michael@0: } else { michael@0: aPluginTag->mUnloadTimer = do_CreateInstance(NS_TIMER_CONTRACTID); michael@0: } michael@0: aPluginTag->mUnloadTimer->InitWithCallback(this, 1000 * 60 * 3, nsITimer::TYPE_ONE_SHOT); michael@0: } michael@0: } michael@0: } michael@0: michael@0: nsresult michael@0: nsPluginHost::GetPluginTempDir(nsIFile **aDir) michael@0: { michael@0: if (!sPluginTempDir) { michael@0: nsCOMPtr tmpDir; michael@0: nsresult rv = NS_GetSpecialDirectory(NS_OS_TEMP_DIR, michael@0: getter_AddRefs(tmpDir)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: rv = tmpDir->AppendNative(kPluginTmpDirName); michael@0: michael@0: // make it unique, and mode == 0700, not world-readable michael@0: rv = tmpDir->CreateUnique(nsIFile::DIRECTORY_TYPE, 0700); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: tmpDir.swap(sPluginTempDir); michael@0: } michael@0: michael@0: return sPluginTempDir->Clone(aDir); michael@0: } michael@0: michael@0: nsresult michael@0: nsPluginHost::InstantiatePluginInstance(const char *aMimeType, nsIURI* aURL, michael@0: nsObjectLoadingContent *aContent, michael@0: nsPluginInstanceOwner** aOwner) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aOwner); michael@0: michael@0: #ifdef PLUGIN_LOGGING michael@0: nsAutoCString urlSpec; michael@0: if (aURL) michael@0: aURL->GetAsciiSpec(urlSpec); michael@0: michael@0: PR_LOG(nsPluginLogging::gPluginLog, PLUGIN_LOG_NORMAL, michael@0: ("nsPluginHost::InstantiatePlugin Begin mime=%s, url=%s\n", michael@0: aMimeType, urlSpec.get())); michael@0: michael@0: PR_LogFlush(); michael@0: #endif michael@0: michael@0: if (!aMimeType) { michael@0: NS_NOTREACHED("Attempting to spawn a plugin with no mime type"); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: nsRefPtr instanceOwner = new nsPluginInstanceOwner(); michael@0: if (!instanceOwner) { michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: } michael@0: michael@0: nsCOMPtr ourContent = do_QueryInterface(static_cast(aContent)); michael@0: nsresult rv = instanceOwner->Init(ourContent); michael@0: if (NS_FAILED(rv)) { michael@0: return rv; michael@0: } michael@0: michael@0: nsPluginTagType tagType; michael@0: rv = instanceOwner->GetTagType(&tagType); michael@0: if (NS_FAILED(rv)) { michael@0: return rv; michael@0: } michael@0: michael@0: if (tagType != nsPluginTagType_Embed && michael@0: tagType != nsPluginTagType_Applet && michael@0: tagType != nsPluginTagType_Object) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: rv = SetUpPluginInstance(aMimeType, aURL, instanceOwner); michael@0: if (NS_FAILED(rv)) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: nsRefPtr instance; michael@0: rv = instanceOwner->GetInstance(getter_AddRefs(instance)); michael@0: if (NS_FAILED(rv)) { michael@0: return rv; michael@0: } michael@0: michael@0: if (instance) { michael@0: instanceOwner->CreateWidget(); michael@0: michael@0: // If we've got a native window, the let the plugin know about it. michael@0: instanceOwner->CallSetWindow(); michael@0: } michael@0: michael@0: // At this point we consider instantiation to be successful. Do not return an error. michael@0: instanceOwner.forget(aOwner); michael@0: michael@0: #ifdef PLUGIN_LOGGING michael@0: nsAutoCString urlSpec2; michael@0: if (aURL != nullptr) aURL->GetAsciiSpec(urlSpec2); michael@0: michael@0: PR_LOG(nsPluginLogging::gPluginLog, PLUGIN_LOG_NORMAL, michael@0: ("nsPluginHost::InstantiatePlugin Finished mime=%s, rv=%d, url=%s\n", michael@0: aMimeType, rv, urlSpec2.get())); michael@0: michael@0: PR_LogFlush(); michael@0: #endif michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsPluginTag* michael@0: nsPluginHost::FindTagForLibrary(PRLibrary* aLibrary) michael@0: { michael@0: nsPluginTag* pluginTag; michael@0: for (pluginTag = mPlugins; pluginTag; pluginTag = pluginTag->mNext) { michael@0: if (pluginTag->mLibrary == aLibrary) { michael@0: return pluginTag; michael@0: } michael@0: } michael@0: return nullptr; michael@0: } michael@0: michael@0: nsPluginTag* michael@0: nsPluginHost::TagForPlugin(nsNPAPIPlugin* aPlugin) michael@0: { michael@0: nsPluginTag* pluginTag; michael@0: for (pluginTag = mPlugins; pluginTag; pluginTag = pluginTag->mNext) { michael@0: if (pluginTag->mPlugin == aPlugin) { michael@0: return pluginTag; michael@0: } michael@0: } michael@0: // a plugin should never exist without a corresponding tag michael@0: NS_ERROR("TagForPlugin has failed"); michael@0: return nullptr; michael@0: } michael@0: michael@0: nsresult nsPluginHost::SetUpPluginInstance(const char *aMimeType, michael@0: nsIURI *aURL, michael@0: nsPluginInstanceOwner *aOwner) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aOwner); michael@0: michael@0: nsresult rv = TrySetUpPluginInstance(aMimeType, aURL, aOwner); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: return rv; michael@0: } michael@0: michael@0: // If we failed to load a plugin instance we'll try again after michael@0: // reloading our plugin list. Only do that once per document to michael@0: // avoid redundant high resource usage on pages with multiple michael@0: // unkown instance types. We'll do that by caching the document. michael@0: nsCOMPtr document; michael@0: aOwner->GetDocument(getter_AddRefs(document)); michael@0: michael@0: nsCOMPtr currentdocument = do_QueryReferent(mCurrentDocument); michael@0: if (document == currentdocument) { michael@0: return rv; michael@0: } michael@0: michael@0: mCurrentDocument = do_GetWeakReference(document); michael@0: michael@0: // Don't try to set up an instance again if nothing changed. michael@0: if (ReloadPlugins() == NS_ERROR_PLUGINS_PLUGINSNOTCHANGED) { michael@0: return rv; michael@0: } michael@0: michael@0: return TrySetUpPluginInstance(aMimeType, aURL, aOwner); michael@0: } michael@0: michael@0: nsresult michael@0: nsPluginHost::TrySetUpPluginInstance(const char *aMimeType, michael@0: nsIURI *aURL, michael@0: nsPluginInstanceOwner *aOwner) michael@0: { michael@0: #ifdef PLUGIN_LOGGING michael@0: nsAutoCString urlSpec; michael@0: if (aURL != nullptr) aURL->GetSpec(urlSpec); michael@0: michael@0: PR_LOG(nsPluginLogging::gPluginLog, PLUGIN_LOG_NORMAL, michael@0: ("nsPluginHost::TrySetupPluginInstance Begin mime=%s, owner=%p, url=%s\n", michael@0: aMimeType, aOwner, urlSpec.get())); michael@0: michael@0: PR_LogFlush(); michael@0: #endif michael@0: michael@0: nsRefPtr plugin; michael@0: GetPlugin(aMimeType, getter_AddRefs(plugin)); michael@0: if (!plugin) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: nsPluginTag* pluginTag = FindPluginForType(aMimeType, true); michael@0: michael@0: NS_ASSERTION(pluginTag, "Must have plugin tag here!"); michael@0: michael@0: #if defined(MOZ_WIDGET_ANDROID) && defined(MOZ_CRASHREPORTER) michael@0: if (pluginTag->mIsFlashPlugin) { michael@0: CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("FlashVersion"), pluginTag->mVersion); michael@0: } michael@0: #endif michael@0: michael@0: nsRefPtr instance = new nsNPAPIPluginInstance(); michael@0: michael@0: // This will create the owning reference. The connection must be made between the michael@0: // instance and the instance owner before initialization. Plugins can call into michael@0: // the browser during initialization. michael@0: aOwner->SetInstance(instance.get()); michael@0: michael@0: // Add the instance to the instances list before we call NPP_New so that michael@0: // it is "in play" before NPP_New happens. Take it out if NPP_New fails. michael@0: mInstances.AppendElement(instance.get()); michael@0: michael@0: // this should not addref the instance or owner michael@0: // except in some cases not Java, see bug 140931 michael@0: // our COM pointer will free the peer michael@0: nsresult rv = instance->Initialize(plugin.get(), aOwner, aMimeType); michael@0: if (NS_FAILED(rv)) { michael@0: mInstances.RemoveElement(instance.get()); michael@0: aOwner->SetInstance(nullptr); michael@0: return rv; michael@0: } michael@0: michael@0: // Cancel the plugin unload timer since we are creating michael@0: // an instance for it. michael@0: if (pluginTag->mUnloadTimer) { michael@0: pluginTag->mUnloadTimer->Cancel(); michael@0: } michael@0: michael@0: #ifdef PLUGIN_LOGGING michael@0: nsAutoCString urlSpec2; michael@0: if (aURL) michael@0: aURL->GetSpec(urlSpec2); michael@0: michael@0: PR_LOG(nsPluginLogging::gPluginLog, PLUGIN_LOG_BASIC, michael@0: ("nsPluginHost::TrySetupPluginInstance Finished mime=%s, rv=%d, owner=%p, url=%s\n", michael@0: aMimeType, rv, aOwner, urlSpec2.get())); michael@0: michael@0: PR_LogFlush(); michael@0: #endif michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: bool michael@0: nsPluginHost::PluginExistsForType(const char* aMimeType) michael@0: { michael@0: nsPluginTag *plugin = FindPluginForType(aMimeType, false); michael@0: return nullptr != plugin; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsPluginHost::GetPluginTagForType(const nsACString& aMimeType, michael@0: nsIPluginTag** aResult) michael@0: { michael@0: nsPluginTag* plugin = FindPluginForType(aMimeType.Data(), true); michael@0: if (!plugin) { michael@0: plugin = FindPluginForType(aMimeType.Data(), false); michael@0: } michael@0: if (!plugin) { michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: } michael@0: NS_ADDREF(*aResult = plugin); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsPluginHost::GetStateForType(const nsACString &aMimeType, uint32_t* aResult) michael@0: { michael@0: nsPluginTag *plugin = FindPluginForType(aMimeType.Data(), true); michael@0: if (!plugin) { michael@0: plugin = FindPluginForType(aMimeType.Data(), false); michael@0: } michael@0: if (!plugin) { michael@0: return NS_ERROR_UNEXPECTED; michael@0: } michael@0: michael@0: return plugin->GetEnabledState(aResult); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsPluginHost::GetBlocklistStateForType(const char *aMimeType, uint32_t *aState) michael@0: { michael@0: nsPluginTag *plugin = FindPluginForType(aMimeType, true); michael@0: if (!plugin) { michael@0: plugin = FindPluginForType(aMimeType, false); michael@0: } michael@0: if (!plugin) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: *aState = plugin->GetBlocklistState(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsPluginHost::GetPermissionStringForType(const nsACString &aMimeType, nsACString &aPermissionString) michael@0: { michael@0: aPermissionString.Truncate(); michael@0: uint32_t blocklistState; michael@0: nsresult rv = GetBlocklistStateForType(aMimeType.Data(), &blocklistState); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: nsPluginTag *tag = FindPluginForType(aMimeType.Data(), true); michael@0: if (!tag) { michael@0: tag = FindPluginForType(aMimeType.Data(), false); michael@0: } michael@0: if (!tag) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: if (blocklistState == nsIBlocklistService::STATE_VULNERABLE_UPDATE_AVAILABLE || michael@0: blocklistState == nsIBlocklistService::STATE_VULNERABLE_NO_UPDATE) { michael@0: aPermissionString.AssignLiteral("plugin-vulnerable:"); michael@0: } michael@0: else { michael@0: aPermissionString.AssignLiteral("plugin:"); michael@0: } michael@0: michael@0: aPermissionString.Append(tag->GetNiceFileName()); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: // check comma delimitered extensions michael@0: static int CompareExtensions(const char *aExtensionList, const char *aExtension) michael@0: { michael@0: if (!aExtensionList || !aExtension) michael@0: return -1; michael@0: michael@0: const char *pExt = aExtensionList; michael@0: const char *pComma = strchr(pExt, ','); michael@0: if (!pComma) michael@0: return PL_strcasecmp(pExt, aExtension); michael@0: michael@0: int extlen = strlen(aExtension); michael@0: while (pComma) { michael@0: int length = pComma - pExt; michael@0: if (length == extlen && 0 == PL_strncasecmp(aExtension, pExt, length)) michael@0: return 0; michael@0: pComma++; michael@0: pExt = pComma; michael@0: pComma = strchr(pExt, ','); michael@0: } michael@0: michael@0: // the last one michael@0: return PL_strcasecmp(pExt, aExtension); michael@0: } michael@0: michael@0: nsresult michael@0: nsPluginHost::IsPluginEnabledForExtension(const char* aExtension, michael@0: const char* &aMimeType) michael@0: { michael@0: nsPluginTag *plugin = FindPluginEnabledForExtension(aExtension, aMimeType); michael@0: if (plugin) michael@0: return NS_OK; michael@0: michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: void michael@0: nsPluginHost::GetPlugins(nsTArray >& aPluginArray) michael@0: { michael@0: aPluginArray.Clear(); michael@0: michael@0: LoadPlugins(); michael@0: michael@0: nsPluginTag* plugin = mPlugins; michael@0: while (plugin != nullptr) { michael@0: if (plugin->IsEnabled()) { michael@0: aPluginArray.AppendElement(plugin); michael@0: } michael@0: plugin = plugin->mNext; michael@0: } michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsPluginHost::GetPluginTags(uint32_t* aPluginCount, nsIPluginTag*** aResults) michael@0: { michael@0: LoadPlugins(); michael@0: michael@0: uint32_t count = 0; michael@0: nsRefPtr plugin = mPlugins; michael@0: while (plugin != nullptr) { michael@0: count++; michael@0: plugin = plugin->mNext; michael@0: } michael@0: michael@0: *aResults = static_cast michael@0: (nsMemory::Alloc(count * sizeof(**aResults))); michael@0: if (!*aResults) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: michael@0: *aPluginCount = count; michael@0: michael@0: plugin = mPlugins; michael@0: for (uint32_t i = 0; i < count; i++) { michael@0: (*aResults)[i] = plugin; michael@0: NS_ADDREF((*aResults)[i]); michael@0: plugin = plugin->mNext; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsPluginTag* michael@0: nsPluginHost::FindPreferredPlugin(const InfallibleTArray& matches) michael@0: { michael@0: // We prefer the plugin with the highest version number. michael@0: /// XXX(johns): This seems to assume the only time multiple plugins will have michael@0: /// the same MIME type is if they're multiple versions of the same michael@0: /// plugin -- but since plugin filenames and pretty names can both michael@0: /// update, it's probably less arbitrary than just going at it michael@0: /// alphabetically. michael@0: michael@0: if (matches.IsEmpty()) { michael@0: return nullptr; michael@0: } michael@0: michael@0: nsPluginTag *preferredPlugin = matches[0]; michael@0: for (unsigned int i = 1; i < matches.Length(); i++) { michael@0: if (mozilla::Version(matches[i]->mVersion.get()) > preferredPlugin->mVersion.get()) { michael@0: preferredPlugin = matches[i]; michael@0: } michael@0: } michael@0: michael@0: return preferredPlugin; michael@0: } michael@0: michael@0: nsPluginTag* michael@0: nsPluginHost::FindPluginForType(const char* aMimeType, michael@0: bool aCheckEnabled) michael@0: { michael@0: if (!aMimeType) { michael@0: return nullptr; michael@0: } michael@0: michael@0: LoadPlugins(); michael@0: michael@0: InfallibleTArray matchingPlugins; michael@0: michael@0: nsPluginTag *plugin = mPlugins; michael@0: while (plugin) { michael@0: if (!aCheckEnabled || plugin->IsActive()) { michael@0: int32_t mimeCount = plugin->mMimeTypes.Length(); michael@0: for (int32_t i = 0; i < mimeCount; i++) { michael@0: if (0 == PL_strcasecmp(plugin->mMimeTypes[i].get(), aMimeType)) { michael@0: matchingPlugins.AppendElement(plugin); michael@0: break; michael@0: } michael@0: } michael@0: } michael@0: plugin = plugin->mNext; michael@0: } michael@0: michael@0: return FindPreferredPlugin(matchingPlugins); michael@0: } michael@0: michael@0: nsPluginTag* michael@0: nsPluginHost::FindPluginEnabledForExtension(const char* aExtension, michael@0: const char*& aMimeType) michael@0: { michael@0: if (!aExtension) { michael@0: return nullptr; michael@0: } michael@0: michael@0: LoadPlugins(); michael@0: michael@0: InfallibleTArray matchingPlugins; michael@0: michael@0: nsPluginTag *plugin = mPlugins; michael@0: while (plugin) { michael@0: if (plugin->IsActive()) { michael@0: int32_t variants = plugin->mExtensions.Length(); michael@0: for (int32_t i = 0; i < variants; i++) { michael@0: // mExtensionsArray[cnt] is a list of extensions separated by commas michael@0: if (0 == CompareExtensions(plugin->mExtensions[i].get(), aExtension)) { michael@0: matchingPlugins.AppendElement(plugin); michael@0: break; michael@0: } michael@0: } michael@0: } michael@0: plugin = plugin->mNext; michael@0: } michael@0: michael@0: nsPluginTag *preferredPlugin = FindPreferredPlugin(matchingPlugins); michael@0: if (!preferredPlugin) { michael@0: return nullptr; michael@0: } michael@0: michael@0: int32_t variants = preferredPlugin->mExtensions.Length(); michael@0: for (int32_t i = 0; i < variants; i++) { michael@0: // mExtensionsArray[cnt] is a list of extensions separated by commas michael@0: if (0 == CompareExtensions(preferredPlugin->mExtensions[i].get(), aExtension)) { michael@0: aMimeType = preferredPlugin->mMimeTypes[i].get(); michael@0: break; michael@0: } michael@0: } michael@0: michael@0: return preferredPlugin; michael@0: } michael@0: michael@0: static nsresult CreateNPAPIPlugin(nsPluginTag *aPluginTag, michael@0: nsNPAPIPlugin **aOutNPAPIPlugin) michael@0: { michael@0: // If this is an in-process plugin we'll need to load it here if we haven't already. michael@0: if (!nsNPAPIPlugin::RunPluginOOP(aPluginTag)) { michael@0: if (aPluginTag->mFullPath.IsEmpty()) michael@0: return NS_ERROR_FAILURE; michael@0: nsCOMPtr file = do_CreateInstance("@mozilla.org/file/local;1"); michael@0: file->InitWithPath(NS_ConvertUTF8toUTF16(aPluginTag->mFullPath)); michael@0: nsPluginFile pluginFile(file); michael@0: PRLibrary* pluginLibrary = nullptr; michael@0: michael@0: if (NS_FAILED(pluginFile.LoadPlugin(&pluginLibrary)) || !pluginLibrary) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: aPluginTag->mLibrary = pluginLibrary; michael@0: } michael@0: michael@0: nsresult rv; michael@0: rv = nsNPAPIPlugin::CreatePlugin(aPluginTag, aOutNPAPIPlugin); michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: nsresult nsPluginHost::EnsurePluginLoaded(nsPluginTag* aPluginTag) michael@0: { michael@0: nsRefPtr plugin = aPluginTag->mPlugin; michael@0: if (!plugin) { michael@0: nsresult rv = CreateNPAPIPlugin(aPluginTag, getter_AddRefs(plugin)); michael@0: if (NS_FAILED(rv)) { michael@0: return rv; michael@0: } michael@0: aPluginTag->mPlugin = plugin; michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult nsPluginHost::GetPlugin(const char *aMimeType, nsNPAPIPlugin** aPlugin) michael@0: { michael@0: nsresult rv = NS_ERROR_FAILURE; michael@0: *aPlugin = nullptr; michael@0: michael@0: if (!aMimeType) michael@0: return NS_ERROR_ILLEGAL_VALUE; michael@0: michael@0: // If plugins haven't been scanned yet, do so now michael@0: LoadPlugins(); michael@0: michael@0: nsPluginTag* pluginTag = FindPluginForType(aMimeType, true); michael@0: if (pluginTag) { michael@0: rv = NS_OK; michael@0: PLUGIN_LOG(PLUGIN_LOG_BASIC, michael@0: ("nsPluginHost::GetPlugin Begin mime=%s, plugin=%s\n", michael@0: aMimeType, pluginTag->mFileName.get())); michael@0: michael@0: #ifdef DEBUG michael@0: if (aMimeType && !pluginTag->mFileName.IsEmpty()) michael@0: printf("For %s found plugin %s\n", aMimeType, pluginTag->mFileName.get()); michael@0: #endif michael@0: michael@0: rv = EnsurePluginLoaded(pluginTag); michael@0: if (NS_FAILED(rv)) { michael@0: return rv; michael@0: } michael@0: michael@0: NS_ADDREF(*aPlugin = pluginTag->mPlugin); michael@0: return NS_OK; michael@0: } michael@0: michael@0: PLUGIN_LOG(PLUGIN_LOG_NORMAL, michael@0: ("nsPluginHost::GetPlugin End mime=%s, rv=%d, plugin=%p name=%s\n", michael@0: aMimeType, rv, *aPlugin, michael@0: (pluginTag ? pluginTag->mFileName.get() : "(not found)"))); michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: // Normalize 'host' to ACE. michael@0: nsresult michael@0: nsPluginHost::NormalizeHostname(nsCString& host) michael@0: { michael@0: if (IsASCII(host)) { michael@0: ToLowerCase(host); michael@0: return NS_OK; michael@0: } michael@0: michael@0: if (!mIDNService) { michael@0: nsresult rv; michael@0: mIDNService = do_GetService(NS_IDNSERVICE_CONTRACTID, &rv); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: michael@0: return mIDNService->ConvertUTF8toACE(host, host); michael@0: } michael@0: michael@0: // Enumerate a 'sites' array returned by GetSitesWithData and determine if michael@0: // any of them have a base domain in common with 'domain'; if so, append them michael@0: // to the 'result' array. If 'firstMatchOnly' is true, return after finding the michael@0: // first match. michael@0: nsresult michael@0: nsPluginHost::EnumerateSiteData(const nsACString& domain, michael@0: const InfallibleTArray& sites, michael@0: InfallibleTArray& result, michael@0: bool firstMatchOnly) michael@0: { michael@0: NS_ASSERTION(!domain.IsVoid(), "null domain string"); michael@0: michael@0: nsresult rv; michael@0: if (!mTLDService) { michael@0: mTLDService = do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID, &rv); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: michael@0: // Get the base domain from the domain. michael@0: nsCString baseDomain; michael@0: rv = mTLDService->GetBaseDomainFromHost(domain, 0, baseDomain); michael@0: bool isIP = rv == NS_ERROR_HOST_IS_IP_ADDRESS; michael@0: if (isIP || rv == NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS) { michael@0: // The base domain is the site itself. However, we must be careful to michael@0: // normalize. michael@0: baseDomain = domain; michael@0: rv = NormalizeHostname(baseDomain); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } else if (NS_FAILED(rv)) { michael@0: return rv; michael@0: } michael@0: michael@0: // Enumerate the array of sites with data. michael@0: for (uint32_t i = 0; i < sites.Length(); ++i) { michael@0: const nsCString& site = sites[i]; michael@0: michael@0: // Check if the site is an IP address. michael@0: bool siteIsIP = michael@0: site.Length() >= 2 && site.First() == '[' && site.Last() == ']'; michael@0: if (siteIsIP != isIP) michael@0: continue; michael@0: michael@0: nsCString siteBaseDomain; michael@0: if (siteIsIP) { michael@0: // Strip the '[]'. michael@0: siteBaseDomain = Substring(site, 1, site.Length() - 2); michael@0: } else { michael@0: // Determine the base domain of the site. michael@0: rv = mTLDService->GetBaseDomainFromHost(site, 0, siteBaseDomain); michael@0: if (rv == NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS) { michael@0: // The base domain is the site itself. However, we must be careful to michael@0: // normalize. michael@0: siteBaseDomain = site; michael@0: rv = NormalizeHostname(siteBaseDomain); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } else if (NS_FAILED(rv)) { michael@0: return rv; michael@0: } michael@0: } michael@0: michael@0: // At this point, we can do an exact comparison of the two domains. michael@0: if (baseDomain != siteBaseDomain) { michael@0: continue; michael@0: } michael@0: michael@0: // Append the site to the result array. michael@0: result.AppendElement(site); michael@0: michael@0: // If we're supposed to return early, do so. michael@0: if (firstMatchOnly) { michael@0: break; michael@0: } michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsPluginHost::RegisterPlayPreviewMimeType(const nsACString& mimeType, michael@0: bool ignoreCTP, michael@0: const nsACString& redirectURL) michael@0: { michael@0: nsAutoCString mt(mimeType); michael@0: nsAutoCString url(redirectURL); michael@0: if (url.Length() == 0) { michael@0: // using default play preview iframe URL, if redirectURL is not specified michael@0: url.Assign("data:application/x-moz-playpreview;,"); michael@0: url.Append(mimeType); michael@0: } michael@0: michael@0: nsRefPtr playPreview = michael@0: new nsPluginPlayPreviewInfo(mt.get(), ignoreCTP, url.get()); michael@0: mPlayPreviewMimeTypes.AppendElement(playPreview); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsPluginHost::UnregisterPlayPreviewMimeType(const nsACString& mimeType) michael@0: { michael@0: nsAutoCString mimeTypeToRemove(mimeType); michael@0: for (uint32_t i = mPlayPreviewMimeTypes.Length(); i > 0; i--) { michael@0: nsRefPtr pp = mPlayPreviewMimeTypes[i - 1]; michael@0: if (PL_strcasecmp(pp.get()->mMimeType.get(), mimeTypeToRemove.get()) == 0) { michael@0: mPlayPreviewMimeTypes.RemoveElementAt(i - 1); michael@0: break; michael@0: } michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsPluginHost::GetPlayPreviewInfo(const nsACString& mimeType, michael@0: nsIPluginPlayPreviewInfo** aResult) michael@0: { michael@0: nsAutoCString mimeTypeToFind(mimeType); michael@0: for (uint32_t i = 0; i < mPlayPreviewMimeTypes.Length(); i++) { michael@0: nsRefPtr pp = mPlayPreviewMimeTypes[i]; michael@0: if (PL_strcasecmp(pp.get()->mMimeType.get(), mimeTypeToFind.get()) == 0) { michael@0: *aResult = new nsPluginPlayPreviewInfo(pp.get()); michael@0: NS_ADDREF(*aResult); michael@0: return NS_OK; michael@0: } michael@0: } michael@0: *aResult = nullptr; michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsPluginHost::ClearSiteData(nsIPluginTag* plugin, const nsACString& domain, michael@0: uint64_t flags, int64_t maxAge) michael@0: { michael@0: // maxAge must be either a nonnegative integer or -1. michael@0: NS_ENSURE_ARG(maxAge >= 0 || maxAge == -1); michael@0: michael@0: // Caller may give us a tag object that is no longer live. michael@0: if (!IsLiveTag(plugin)) { michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: } michael@0: michael@0: nsPluginTag* tag = static_cast(plugin); michael@0: michael@0: // We only ensure support for clearing Flash site data for now. michael@0: // We will also attempt to clear data for any plugin that happens michael@0: // to be loaded already. michael@0: if (!tag->mIsFlashPlugin && !tag->mPlugin) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: // Make sure the plugin is loaded. michael@0: nsresult rv = EnsurePluginLoaded(tag); michael@0: if (NS_FAILED(rv)) { michael@0: return rv; michael@0: } michael@0: michael@0: PluginLibrary* library = tag->mPlugin->GetLibrary(); michael@0: michael@0: // If 'domain' is the null string, clear everything. michael@0: if (domain.IsVoid()) { michael@0: return library->NPP_ClearSiteData(nullptr, flags, maxAge); michael@0: } michael@0: michael@0: // Get the list of sites from the plugin. michael@0: InfallibleTArray sites; michael@0: rv = library->NPP_GetSitesWithData(sites); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: // Enumerate the sites and build a list of matches. michael@0: InfallibleTArray matches; michael@0: rv = EnumerateSiteData(domain, sites, matches, false); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: // Clear the matches. michael@0: for (uint32_t i = 0; i < matches.Length(); ++i) { michael@0: const nsCString& match = matches[i]; michael@0: rv = library->NPP_ClearSiteData(match.get(), flags, maxAge); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsPluginHost::SiteHasData(nsIPluginTag* plugin, const nsACString& domain, michael@0: bool* result) michael@0: { michael@0: // Caller may give us a tag object that is no longer live. michael@0: if (!IsLiveTag(plugin)) { michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: } michael@0: michael@0: nsPluginTag* tag = static_cast(plugin); michael@0: michael@0: // We only ensure support for clearing Flash site data for now. michael@0: // We will also attempt to clear data for any plugin that happens michael@0: // to be loaded already. michael@0: if (!tag->mIsFlashPlugin && !tag->mPlugin) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: // Make sure the plugin is loaded. michael@0: nsresult rv = EnsurePluginLoaded(tag); michael@0: if (NS_FAILED(rv)) { michael@0: return rv; michael@0: } michael@0: michael@0: PluginLibrary* library = tag->mPlugin->GetLibrary(); michael@0: michael@0: // Get the list of sites from the plugin. michael@0: InfallibleTArray sites; michael@0: rv = library->NPP_GetSitesWithData(sites); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: // If there's no data, we're done. michael@0: if (sites.IsEmpty()) { michael@0: *result = false; michael@0: return NS_OK; michael@0: } michael@0: michael@0: // If 'domain' is the null string, and there's data for at least one site, michael@0: // we're done. michael@0: if (domain.IsVoid()) { michael@0: *result = true; michael@0: return NS_OK; michael@0: } michael@0: michael@0: // Enumerate the sites and determine if there's a match. michael@0: InfallibleTArray matches; michael@0: rv = EnumerateSiteData(domain, sites, matches, true); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: *result = !matches.IsEmpty(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: bool nsPluginHost::IsJavaMIMEType(const char* aType) michael@0: { michael@0: // The java mime pref may well not be one of these, michael@0: // e.g. application/x-java-test used in the test suite michael@0: nsAdoptingCString javaMIME = Preferences::GetCString(kPrefJavaMIME); michael@0: return aType && michael@0: (javaMIME.EqualsIgnoreCase(aType) || michael@0: (0 == PL_strncasecmp(aType, "application/x-java-vm", michael@0: sizeof("application/x-java-vm") - 1)) || michael@0: (0 == PL_strncasecmp(aType, "application/x-java-applet", michael@0: sizeof("application/x-java-applet") - 1)) || michael@0: (0 == PL_strncasecmp(aType, "application/x-java-bean", michael@0: sizeof("application/x-java-bean") - 1))); michael@0: } michael@0: michael@0: // Check whether or not a tag is a live, valid tag, and that it's loaded. michael@0: bool michael@0: nsPluginHost::IsLiveTag(nsIPluginTag* aPluginTag) michael@0: { michael@0: nsPluginTag* tag; michael@0: for (tag = mPlugins; tag; tag = tag->mNext) { michael@0: if (tag == aPluginTag) { michael@0: return true; michael@0: } michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: nsPluginTag* michael@0: nsPluginHost::HaveSamePlugin(const nsPluginTag* aPluginTag) michael@0: { michael@0: for (nsPluginTag* tag = mPlugins; tag; tag = tag->mNext) { michael@0: if (tag->HasSameNameAndMimes(aPluginTag)) { michael@0: return tag; michael@0: } michael@0: } michael@0: return nullptr; michael@0: } michael@0: michael@0: nsPluginTag* michael@0: nsPluginHost::FirstPluginWithPath(const nsCString& path) michael@0: { michael@0: for (nsPluginTag* tag = mPlugins; tag; tag = tag->mNext) { michael@0: if (tag->mFullPath.Equals(path)) { michael@0: return tag; michael@0: } michael@0: } michael@0: return nullptr; michael@0: } michael@0: michael@0: namespace { michael@0: michael@0: int64_t GetPluginLastModifiedTime(const nsCOMPtr& localfile) michael@0: { michael@0: PRTime fileModTime = 0; michael@0: michael@0: #if defined(XP_MACOSX) michael@0: // On OS X the date of a bundle's "contents" (i.e. of its Info.plist file) michael@0: // is a much better guide to when it was last modified than the date of michael@0: // its package directory. See bug 313700. michael@0: nsCOMPtr localFileMac = do_QueryInterface(localfile); michael@0: if (localFileMac) { michael@0: localFileMac->GetBundleContentsLastModifiedTime(&fileModTime); michael@0: } else { michael@0: localfile->GetLastModifiedTime(&fileModTime); michael@0: } michael@0: #else michael@0: localfile->GetLastModifiedTime(&fileModTime); michael@0: #endif michael@0: michael@0: return fileModTime; michael@0: } michael@0: michael@0: bool michael@0: GetPluginIsFromExtension(const nsCOMPtr& pluginFile, michael@0: const nsCOMArray& extensionDirs) michael@0: { michael@0: for (uint32_t i = 0; i < extensionDirs.Length(); ++i) { michael@0: bool contains; michael@0: if (NS_FAILED(extensionDirs[i]->Contains(pluginFile, true, &contains)) || !contains) { michael@0: continue; michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: return false; michael@0: } michael@0: michael@0: void michael@0: GetExtensionDirectories(nsCOMArray& dirs) michael@0: { michael@0: nsCOMPtr dirService = do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID); michael@0: if (!dirService) { michael@0: return; michael@0: } michael@0: michael@0: nsCOMPtr list; michael@0: nsresult rv = dirService->Get(XRE_EXTENSIONS_DIR_LIST, michael@0: NS_GET_IID(nsISimpleEnumerator), michael@0: getter_AddRefs(list)); michael@0: if (NS_FAILED(rv)) { michael@0: return; michael@0: } michael@0: michael@0: bool more; michael@0: while (NS_SUCCEEDED(list->HasMoreElements(&more)) && more) { michael@0: nsCOMPtr next; michael@0: if (NS_FAILED(list->GetNext(getter_AddRefs(next)))) { michael@0: break; michael@0: } michael@0: nsCOMPtr file = do_QueryInterface(next); michael@0: if (file) { michael@0: file->Normalize(); michael@0: dirs.AppendElement(file); michael@0: } michael@0: } michael@0: } michael@0: michael@0: struct CompareFilesByTime michael@0: { michael@0: bool michael@0: LessThan(const nsCOMPtr& a, const nsCOMPtr& b) const michael@0: { michael@0: return GetPluginLastModifiedTime(a) < GetPluginLastModifiedTime(b); michael@0: } michael@0: michael@0: bool michael@0: Equals(const nsCOMPtr& a, const nsCOMPtr& b) const michael@0: { michael@0: return GetPluginLastModifiedTime(a) == GetPluginLastModifiedTime(b); michael@0: } michael@0: }; michael@0: michael@0: } // anonymous namespace michael@0: michael@0: PRBool nsPluginHost::GhettoBlacklist(nsIFile *pluginFile) michael@0: { michael@0: nsCString leaf; michael@0: const char *leafStr; michael@0: nsresult rv; michael@0: michael@0: rv = pluginFile->GetNativeLeafName(leaf); michael@0: if (NS_FAILED(rv)) { michael@0: return PR_TRUE; // fuck 'em. blacklist. michael@0: } michael@0: michael@0: leafStr = leaf.get(); michael@0: michael@0: if (!leafStr) { michael@0: return PR_TRUE; // fuck 'em. blacklist. michael@0: } michael@0: michael@0: // libgnashplugin.so, libflashplayer.so, Flash Player-10.4-10.5.plugin, michael@0: // NPSWF32.dll, NPSWF64.dll michael@0: if (strstr(leafStr, "libgnashplugin") == leafStr || michael@0: strstr(leafStr, "libflashplayer") == leafStr || michael@0: strstr(leafStr, "Flash Player") == leafStr || michael@0: strstr(leafStr, "NPSWF") == leafStr) { michael@0: return PR_FALSE; michael@0: } michael@0: michael@0: return PR_TRUE; // fuck 'em. blacklist. michael@0: } michael@0: michael@0: typedef NS_NPAPIPLUGIN_CALLBACK(char *, NP_GETMIMEDESCRIPTION)(void); michael@0: michael@0: nsresult nsPluginHost::ScanPluginsDirectory(nsIFile *pluginsDir, michael@0: bool aCreatePluginList, michael@0: bool *aPluginsChanged) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aPluginsChanged); michael@0: nsresult rv; michael@0: michael@0: *aPluginsChanged = false; michael@0: michael@0: #ifdef PLUGIN_LOGGING michael@0: nsAutoCString dirPath; michael@0: pluginsDir->GetNativePath(dirPath); michael@0: PLUGIN_LOG(PLUGIN_LOG_BASIC, michael@0: ("nsPluginHost::ScanPluginsDirectory dir=%s\n", dirPath.get())); michael@0: #endif michael@0: michael@0: nsCOMPtr iter; michael@0: rv = pluginsDir->GetDirectoryEntries(getter_AddRefs(iter)); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: nsAutoTArray, 6> pluginFiles; michael@0: michael@0: bool hasMore; michael@0: while (NS_SUCCEEDED(iter->HasMoreElements(&hasMore)) && hasMore) { michael@0: nsCOMPtr supports; michael@0: rv = iter->GetNext(getter_AddRefs(supports)); michael@0: if (NS_FAILED(rv)) michael@0: continue; michael@0: nsCOMPtr dirEntry(do_QueryInterface(supports, &rv)); michael@0: if (NS_FAILED(rv)) michael@0: continue; michael@0: michael@0: // Sun's JRE 1.3.1 plugin must have symbolic links resolved or else it'll crash. michael@0: // See bug 197855. michael@0: dirEntry->Normalize(); michael@0: michael@0: if (nsPluginsDir::IsPluginFile(dirEntry)) { michael@0: pluginFiles.AppendElement(dirEntry); michael@0: } michael@0: } michael@0: michael@0: pluginFiles.Sort(CompareFilesByTime()); michael@0: michael@0: nsCOMArray extensionDirs; michael@0: GetExtensionDirectories(extensionDirs); michael@0: michael@0: bool warnOutdated = false; michael@0: michael@0: for (int32_t i = (pluginFiles.Length() - 1); i >= 0; i--) { michael@0: nsCOMPtr& localfile = pluginFiles[i]; michael@0: michael@0: nsString utf16FilePath; michael@0: rv = localfile->GetPath(utf16FilePath); michael@0: if (NS_FAILED(rv)) michael@0: continue; michael@0: michael@0: const int64_t fileModTime = GetPluginLastModifiedTime(localfile); michael@0: const bool fromExtension = GetPluginIsFromExtension(localfile, extensionDirs); michael@0: michael@0: // Look for it in our cache michael@0: NS_ConvertUTF16toUTF8 filePath(utf16FilePath); michael@0: nsRefPtr pluginTag; michael@0: RemoveCachedPluginsInfo(filePath.get(), getter_AddRefs(pluginTag)); michael@0: michael@0: bool seenBefore = false; michael@0: michael@0: if (pluginTag) { michael@0: seenBefore = true; michael@0: // If plugin changed, delete cachedPluginTag and don't use cache michael@0: if (fileModTime != pluginTag->mLastModifiedTime) { michael@0: // Plugins has changed. Don't use cached plugin info. michael@0: pluginTag = nullptr; michael@0: michael@0: // plugin file changed, flag this fact michael@0: *aPluginsChanged = true; michael@0: } michael@0: michael@0: // If we're not creating a list and we already know something changed then michael@0: // we're done. michael@0: if (!aCreatePluginList) { michael@0: if (*aPluginsChanged) { michael@0: return NS_OK; michael@0: } michael@0: continue; michael@0: } michael@0: } michael@0: michael@0: bool isKnownInvalidPlugin = false; michael@0: for (nsRefPtr invalidPlugins = mInvalidPlugins; michael@0: invalidPlugins; invalidPlugins = invalidPlugins->mNext) { michael@0: // If already marked as invalid, ignore it michael@0: if (invalidPlugins->mFullPath.Equals(filePath.get()) && michael@0: invalidPlugins->mLastModifiedTime == fileModTime) { michael@0: if (aCreatePluginList) { michael@0: invalidPlugins->mSeen = true; michael@0: } michael@0: isKnownInvalidPlugin = true; michael@0: break; michael@0: } michael@0: } michael@0: if (isKnownInvalidPlugin) { michael@0: continue; michael@0: } michael@0: michael@0: if (GhettoBlacklist(localfile)) { michael@0: continue; michael@0: } michael@0: michael@0: // if it is not found in cache info list or has been changed, create a new one michael@0: if (!pluginTag) { michael@0: nsPluginFile pluginFile(localfile); michael@0: michael@0: // create a tag describing this plugin. michael@0: PRLibrary *library = nullptr; michael@0: nsPluginInfo info; michael@0: memset(&info, 0, sizeof(info)); michael@0: nsresult res; michael@0: // Opening a block for the telemetry AutoTimer michael@0: { michael@0: Telemetry::AutoTimer telemetry; michael@0: res = pluginFile.GetPluginInfo(info, &library); michael@0: } michael@0: // if we don't have mime type don't proceed, this is not a plugin michael@0: if (NS_FAILED(res) || !info.fMimeTypeArray) { michael@0: nsRefPtr invalidTag = new nsInvalidPluginTag(filePath.get(), michael@0: fileModTime); michael@0: pluginFile.FreePluginInfo(info); michael@0: michael@0: if (aCreatePluginList) { michael@0: invalidTag->mSeen = true; michael@0: } michael@0: invalidTag->mNext = mInvalidPlugins; michael@0: if (mInvalidPlugins) { michael@0: mInvalidPlugins->mPrev = invalidTag; michael@0: } michael@0: mInvalidPlugins = invalidTag; michael@0: michael@0: // Mark aPluginsChanged so pluginreg is rewritten michael@0: *aPluginsChanged = true; michael@0: continue; michael@0: } michael@0: michael@0: pluginTag = new nsPluginTag(&info, fileModTime, fromExtension); michael@0: pluginFile.FreePluginInfo(info); michael@0: if (!pluginTag) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: michael@0: pluginTag->mLibrary = library; michael@0: uint32_t state = pluginTag->GetBlocklistState(); michael@0: michael@0: // If the blocklist says it is risky and we have never seen this michael@0: // plugin before, then disable it. michael@0: // If the blocklist says this is an outdated plugin, warn about michael@0: // outdated plugins. michael@0: if (state == nsIBlocklistService::STATE_SOFTBLOCKED && !seenBefore) { michael@0: pluginTag->SetEnabledState(nsIPluginTag::STATE_DISABLED); michael@0: } michael@0: if (state == nsIBlocklistService::STATE_OUTDATED && !seenBefore) { michael@0: warnOutdated = true; michael@0: } michael@0: michael@0: // Plugin unloading is tag-based. If we created a new tag and loaded michael@0: // the library in the process then we want to attempt to unload it here. michael@0: // Only do this if the pref is set for aggressive unloading. michael@0: if (UnloadPluginsASAP()) { michael@0: pluginTag->TryUnloadPlugin(false); michael@0: } michael@0: } michael@0: michael@0: // do it if we still want it michael@0: if (!seenBefore) { michael@0: // We have a valid new plugin so report that plugins have changed. michael@0: *aPluginsChanged = true; michael@0: } michael@0: michael@0: // Avoid adding different versions of the same plugin if they are running michael@0: // in-process, otherwise we risk undefined behaviour. michael@0: if (!nsNPAPIPlugin::RunPluginOOP(pluginTag)) { michael@0: if (HaveSamePlugin(pluginTag)) { michael@0: continue; michael@0: } michael@0: } michael@0: michael@0: // Don't add the same plugin again if it hasn't changed michael@0: if (nsPluginTag* duplicate = FirstPluginWithPath(pluginTag->mFullPath)) { michael@0: if (pluginTag->mLastModifiedTime == duplicate->mLastModifiedTime) { michael@0: continue; michael@0: } michael@0: } michael@0: michael@0: // If we're not creating a plugin list, simply looking for changes, michael@0: // then we're done. michael@0: if (!aCreatePluginList) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: // Add plugin tags such that the list is ordered by modification date, michael@0: // newest to oldest. This is ugly, it'd be easier with just about anything michael@0: // other than a single-directional linked list. michael@0: if (mPlugins) { michael@0: nsPluginTag *prev = nullptr; michael@0: nsPluginTag *next = mPlugins; michael@0: while (next) { michael@0: if (pluginTag->mLastModifiedTime >= next->mLastModifiedTime) { michael@0: pluginTag->mNext = next; michael@0: if (prev) { michael@0: prev->mNext = pluginTag; michael@0: } else { michael@0: mPlugins = pluginTag; michael@0: } michael@0: break; michael@0: } michael@0: prev = next; michael@0: next = prev->mNext; michael@0: if (!next) { michael@0: prev->mNext = pluginTag; michael@0: } michael@0: } michael@0: } else { michael@0: mPlugins = pluginTag; michael@0: } michael@0: michael@0: if (pluginTag->IsActive()) { michael@0: nsAdoptingCString disableFullPage = michael@0: Preferences::GetCString(kPrefDisableFullPage); michael@0: for (uint32_t i = 0; i < pluginTag->mMimeTypes.Length(); i++) { michael@0: if (!IsTypeInList(pluginTag->mMimeTypes[i], disableFullPage)) { michael@0: RegisterWithCategoryManager(pluginTag->mMimeTypes[i], michael@0: ePluginRegister); michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: if (warnOutdated) { michael@0: Preferences::SetBool("plugins.update.notifyUser", true); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult nsPluginHost::ScanPluginsDirectoryList(nsISimpleEnumerator *dirEnum, michael@0: bool aCreatePluginList, michael@0: bool *aPluginsChanged) michael@0: { michael@0: bool hasMore; michael@0: while (NS_SUCCEEDED(dirEnum->HasMoreElements(&hasMore)) && hasMore) { michael@0: nsCOMPtr supports; michael@0: nsresult rv = dirEnum->GetNext(getter_AddRefs(supports)); michael@0: if (NS_FAILED(rv)) michael@0: continue; michael@0: nsCOMPtr nextDir(do_QueryInterface(supports, &rv)); michael@0: if (NS_FAILED(rv)) michael@0: continue; michael@0: michael@0: // don't pass aPluginsChanged directly to prevent it from been reset michael@0: bool pluginschanged = false; michael@0: ScanPluginsDirectory(nextDir, aCreatePluginList, &pluginschanged); michael@0: michael@0: if (pluginschanged) michael@0: *aPluginsChanged = true; michael@0: michael@0: // if changes are detected and we are not creating the list, do not proceed michael@0: if (!aCreatePluginList && *aPluginsChanged) michael@0: break; michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult nsPluginHost::LoadPlugins() michael@0: { michael@0: #ifdef ANDROID michael@0: if (XRE_GetProcessType() == GeckoProcessType_Content) { michael@0: return NS_OK; michael@0: } michael@0: #endif michael@0: // do not do anything if it is already done michael@0: // use ReloadPlugins() to enforce loading michael@0: if (mPluginsLoaded) michael@0: return NS_OK; michael@0: michael@0: if (mPluginsDisabled) michael@0: return NS_OK; michael@0: michael@0: bool pluginschanged; michael@0: nsresult rv = FindPlugins(true, &pluginschanged); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: // only if plugins have changed will we notify plugin-change observers michael@0: if (pluginschanged) { michael@0: nsCOMPtr obsService = michael@0: mozilla::services::GetObserverService(); michael@0: if (obsService) michael@0: obsService->NotifyObservers(nullptr, "plugins-list-updated", nullptr); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: // if aCreatePluginList is false we will just scan for plugins michael@0: // and see if any changes have been made to the plugins. michael@0: // This is needed in ReloadPlugins to prevent possible recursive reloads michael@0: nsresult nsPluginHost::FindPlugins(bool aCreatePluginList, bool * aPluginsChanged) michael@0: { michael@0: Telemetry::AutoTimer telemetry; michael@0: michael@0: NS_ENSURE_ARG_POINTER(aPluginsChanged); michael@0: michael@0: *aPluginsChanged = false; michael@0: nsresult rv; michael@0: michael@0: // Read cached plugins info. If the profile isn't yet available then don't michael@0: // scan for plugins michael@0: if (ReadPluginInfo() == NS_ERROR_NOT_AVAILABLE) michael@0: return NS_OK; michael@0: michael@0: #ifdef XP_WIN michael@0: // Failure here is not a show-stopper so just warn. michael@0: rv = EnsurePrivateDirServiceProvider(); michael@0: NS_ASSERTION(NS_SUCCEEDED(rv), "Failed to register dir service provider."); michael@0: #endif /* XP_WIN */ michael@0: michael@0: nsCOMPtr dirService(do_GetService(kDirectoryServiceContractID, &rv)); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: nsCOMPtr dirList; michael@0: michael@0: // Scan plugins directories; michael@0: // don't pass aPluginsChanged directly, to prevent its michael@0: // possible reset in subsequent ScanPluginsDirectory calls michael@0: bool pluginschanged = false; michael@0: michael@0: // Scan the app-defined list of plugin dirs. michael@0: rv = dirService->Get(NS_APP_PLUGINS_DIR_LIST, NS_GET_IID(nsISimpleEnumerator), getter_AddRefs(dirList)); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: ScanPluginsDirectoryList(dirList, aCreatePluginList, &pluginschanged); michael@0: michael@0: if (pluginschanged) michael@0: *aPluginsChanged = true; michael@0: michael@0: // if we are just looking for possible changes, michael@0: // no need to proceed if changes are detected michael@0: if (!aCreatePluginList && *aPluginsChanged) { michael@0: NS_ITERATIVE_UNREF_LIST(nsRefPtr, mCachedPlugins, mNext); michael@0: NS_ITERATIVE_UNREF_LIST(nsRefPtr, mInvalidPlugins, mNext); michael@0: return NS_OK; michael@0: } michael@0: } else { michael@0: #ifdef ANDROID michael@0: LOG("getting plugins dir failed"); michael@0: #endif michael@0: } michael@0: michael@0: mPluginsLoaded = true; // at this point 'some' plugins have been loaded, michael@0: // the rest is optional michael@0: michael@0: #ifdef XP_WIN michael@0: bool bScanPLIDs = Preferences::GetBool("plugin.scan.plid.all", false); michael@0: michael@0: // Now lets scan any PLID directories michael@0: if (bScanPLIDs && mPrivateDirServiceProvider) { michael@0: rv = mPrivateDirServiceProvider->GetPLIDDirectories(getter_AddRefs(dirList)); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: ScanPluginsDirectoryList(dirList, aCreatePluginList, &pluginschanged); michael@0: michael@0: if (pluginschanged) michael@0: *aPluginsChanged = true; michael@0: michael@0: // if we are just looking for possible changes, michael@0: // no need to proceed if changes are detected michael@0: if (!aCreatePluginList && *aPluginsChanged) { michael@0: NS_ITERATIVE_UNREF_LIST(nsRefPtr, mCachedPlugins, mNext); michael@0: NS_ITERATIVE_UNREF_LIST(nsRefPtr, mInvalidPlugins, mNext); michael@0: return NS_OK; michael@0: } michael@0: } michael@0: } michael@0: michael@0: michael@0: // Scan the installation paths of our popular plugins if the prefs are enabled michael@0: michael@0: // This table controls the order of scanning michael@0: const char* const prefs[] = {NS_WIN_ACROBAT_SCAN_KEY, michael@0: NS_WIN_QUICKTIME_SCAN_KEY, michael@0: NS_WIN_WMP_SCAN_KEY}; michael@0: michael@0: uint32_t size = sizeof(prefs) / sizeof(prefs[0]); michael@0: michael@0: for (uint32_t i = 0; i < size; i+=1) { michael@0: nsCOMPtr dirToScan; michael@0: bool bExists; michael@0: if (NS_SUCCEEDED(dirService->Get(prefs[i], NS_GET_IID(nsIFile), getter_AddRefs(dirToScan))) && michael@0: dirToScan && michael@0: NS_SUCCEEDED(dirToScan->Exists(&bExists)) && michael@0: bExists) { michael@0: michael@0: ScanPluginsDirectory(dirToScan, aCreatePluginList, &pluginschanged); michael@0: michael@0: if (pluginschanged) michael@0: *aPluginsChanged = true; michael@0: michael@0: // if we are just looking for possible changes, michael@0: // no need to proceed if changes are detected michael@0: if (!aCreatePluginList && *aPluginsChanged) { michael@0: NS_ITERATIVE_UNREF_LIST(nsRefPtr, mCachedPlugins, mNext); michael@0: NS_ITERATIVE_UNREF_LIST(nsRefPtr, mInvalidPlugins, mNext); michael@0: return NS_OK; michael@0: } michael@0: } michael@0: } michael@0: #endif michael@0: michael@0: // We should also consider plugins to have changed if any plugins have been removed. michael@0: // We'll know if any were removed if they weren't taken out of the cached plugins list michael@0: // during our scan, thus we can assume something was removed if the cached plugins list michael@0: // contains anything. michael@0: if (!*aPluginsChanged && mCachedPlugins) { michael@0: *aPluginsChanged = true; michael@0: } michael@0: michael@0: // Remove unseen invalid plugins michael@0: nsRefPtr invalidPlugins = mInvalidPlugins; michael@0: while (invalidPlugins) { michael@0: if (!invalidPlugins->mSeen) { michael@0: nsRefPtr invalidPlugin = invalidPlugins; michael@0: michael@0: if (invalidPlugin->mPrev) { michael@0: invalidPlugin->mPrev->mNext = invalidPlugin->mNext; michael@0: } michael@0: else { michael@0: mInvalidPlugins = invalidPlugin->mNext; michael@0: } michael@0: if (invalidPlugin->mNext) { michael@0: invalidPlugin->mNext->mPrev = invalidPlugin->mPrev; michael@0: } michael@0: michael@0: invalidPlugins = invalidPlugin->mNext; michael@0: michael@0: invalidPlugin->mPrev = nullptr; michael@0: invalidPlugin->mNext = nullptr; michael@0: } michael@0: else { michael@0: invalidPlugins->mSeen = false; michael@0: invalidPlugins = invalidPlugins->mNext; michael@0: } michael@0: } michael@0: michael@0: // if we are not creating the list, there is no need to proceed michael@0: if (!aCreatePluginList) { michael@0: NS_ITERATIVE_UNREF_LIST(nsRefPtr, mCachedPlugins, mNext); michael@0: NS_ITERATIVE_UNREF_LIST(nsRefPtr, mInvalidPlugins, mNext); michael@0: return NS_OK; michael@0: } michael@0: michael@0: // if we are creating the list, it is already done; michael@0: // update the plugins info cache if changes are detected michael@0: if (*aPluginsChanged) michael@0: WritePluginInfo(); michael@0: michael@0: // No more need for cached plugins. Clear it up. michael@0: NS_ITERATIVE_UNREF_LIST(nsRefPtr, mCachedPlugins, mNext); michael@0: NS_ITERATIVE_UNREF_LIST(nsRefPtr, mInvalidPlugins, mNext); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsPluginHost::UpdatePluginInfo(nsPluginTag* aPluginTag) michael@0: { michael@0: ReadPluginInfo(); michael@0: WritePluginInfo(); michael@0: NS_ITERATIVE_UNREF_LIST(nsRefPtr, mCachedPlugins, mNext); michael@0: NS_ITERATIVE_UNREF_LIST(nsRefPtr, mInvalidPlugins, mNext); michael@0: michael@0: if (!aPluginTag) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: // Update types with category manager michael@0: nsAdoptingCString disableFullPage = michael@0: Preferences::GetCString(kPrefDisableFullPage); michael@0: for (uint32_t i = 0; i < aPluginTag->mMimeTypes.Length(); i++) { michael@0: nsRegisterType shouldRegister; michael@0: michael@0: if (IsTypeInList(aPluginTag->mMimeTypes[i], disableFullPage)) { michael@0: shouldRegister = ePluginUnregister; michael@0: } else { michael@0: nsPluginTag *plugin = FindPluginForType(aPluginTag->mMimeTypes[i].get(), michael@0: true); michael@0: shouldRegister = plugin ? ePluginRegister : ePluginUnregister; michael@0: } michael@0: michael@0: RegisterWithCategoryManager(aPluginTag->mMimeTypes[i], shouldRegister); michael@0: } michael@0: michael@0: nsCOMPtr obsService = michael@0: mozilla::services::GetObserverService(); michael@0: if (obsService) michael@0: obsService->NotifyObservers(nullptr, "plugin-info-updated", nullptr); michael@0: michael@0: // Reload instances if needed michael@0: if (aPluginTag->IsActive()) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* static */ bool michael@0: nsPluginHost::IsTypeWhitelisted(const char *aMimeType) michael@0: { michael@0: nsAdoptingCString whitelist = Preferences::GetCString(kPrefWhitelist); michael@0: if (!whitelist.Length()) { michael@0: return true; michael@0: } michael@0: nsDependentCString wrap(aMimeType); michael@0: return IsTypeInList(wrap, whitelist); michael@0: } michael@0: michael@0: void michael@0: nsPluginHost::RegisterWithCategoryManager(nsCString &aMimeType, michael@0: nsRegisterType aType) michael@0: { michael@0: PLUGIN_LOG(PLUGIN_LOG_NORMAL, michael@0: ("nsPluginTag::RegisterWithCategoryManager type = %s, removing = %s\n", michael@0: aMimeType.get(), aType == ePluginUnregister ? "yes" : "no")); michael@0: michael@0: nsCOMPtr catMan = michael@0: do_GetService(NS_CATEGORYMANAGER_CONTRACTID); michael@0: if (!catMan) { michael@0: return; michael@0: } michael@0: michael@0: const char *contractId = michael@0: "@mozilla.org/content/plugin/document-loader-factory;1"; michael@0: michael@0: if (aType == ePluginRegister) { michael@0: catMan->AddCategoryEntry("Gecko-Content-Viewers", michael@0: aMimeType.get(), michael@0: contractId, michael@0: false, /* persist: broken by bug 193031 */ michael@0: mOverrideInternalTypes, michael@0: nullptr); michael@0: } else { michael@0: // Only delete the entry if a plugin registered for it michael@0: nsXPIDLCString value; michael@0: nsresult rv = catMan->GetCategoryEntry("Gecko-Content-Viewers", michael@0: aMimeType.get(), michael@0: getter_Copies(value)); michael@0: if (NS_SUCCEEDED(rv) && strcmp(value, contractId) == 0) { michael@0: catMan->DeleteCategoryEntry("Gecko-Content-Viewers", michael@0: aMimeType.get(), michael@0: true); michael@0: } michael@0: } michael@0: } michael@0: michael@0: nsresult michael@0: nsPluginHost::WritePluginInfo() michael@0: { michael@0: michael@0: nsresult rv = NS_OK; michael@0: nsCOMPtr directoryService(do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID,&rv)); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: directoryService->Get(NS_APP_USER_PROFILE_50_DIR, NS_GET_IID(nsIFile), michael@0: getter_AddRefs(mPluginRegFile)); michael@0: michael@0: if (!mPluginRegFile) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: PRFileDesc* fd = nullptr; michael@0: michael@0: nsCOMPtr pluginReg; michael@0: michael@0: rv = mPluginRegFile->Clone(getter_AddRefs(pluginReg)); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: nsAutoCString filename(kPluginRegistryFilename); michael@0: filename.Append(".tmp"); michael@0: rv = pluginReg->AppendNative(filename); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: rv = pluginReg->OpenNSPRFileDesc(PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE, 0600, &fd); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: nsCOMPtr runtime = do_GetService("@mozilla.org/xre/runtime;1"); michael@0: if (!runtime) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: nsAutoCString arch; michael@0: rv = runtime->GetXPCOMABI(arch); michael@0: if (NS_FAILED(rv)) { michael@0: return rv; michael@0: } michael@0: michael@0: PR_fprintf(fd, "Generated File. Do not edit.\n"); michael@0: michael@0: PR_fprintf(fd, "\n[HEADER]\nVersion%c%s%c%c\nArch%c%s%c%c\n", michael@0: PLUGIN_REGISTRY_FIELD_DELIMITER, michael@0: kPluginRegistryVersion, michael@0: PLUGIN_REGISTRY_FIELD_DELIMITER, michael@0: PLUGIN_REGISTRY_END_OF_LINE_MARKER, michael@0: PLUGIN_REGISTRY_FIELD_DELIMITER, michael@0: arch.get(), michael@0: PLUGIN_REGISTRY_FIELD_DELIMITER, michael@0: PLUGIN_REGISTRY_END_OF_LINE_MARKER); michael@0: michael@0: // Store all plugins in the mPlugins list - all plugins currently in use. michael@0: PR_fprintf(fd, "\n[PLUGINS]\n"); michael@0: michael@0: for (nsPluginTag *tag = mPlugins; tag; tag = tag->mNext) { michael@0: // store each plugin info into the registry michael@0: // filename & fullpath are on separate line michael@0: // because they can contain field delimiter char michael@0: PR_fprintf(fd, "%s%c%c\n%s%c%c\n%s%c%c\n", michael@0: (tag->mFileName.get()), michael@0: PLUGIN_REGISTRY_FIELD_DELIMITER, michael@0: PLUGIN_REGISTRY_END_OF_LINE_MARKER, michael@0: (tag->mFullPath.get()), michael@0: PLUGIN_REGISTRY_FIELD_DELIMITER, michael@0: PLUGIN_REGISTRY_END_OF_LINE_MARKER, michael@0: (tag->mVersion.get()), michael@0: PLUGIN_REGISTRY_FIELD_DELIMITER, michael@0: PLUGIN_REGISTRY_END_OF_LINE_MARKER); michael@0: michael@0: // lastModifiedTimeStamp|canUnload|tag->mFlags|fromExtension michael@0: PR_fprintf(fd, "%lld%c%d%c%lu%c%d%c%c\n", michael@0: tag->mLastModifiedTime, michael@0: PLUGIN_REGISTRY_FIELD_DELIMITER, michael@0: false, // did store whether or not to unload in-process plugins michael@0: PLUGIN_REGISTRY_FIELD_DELIMITER, michael@0: 0, // legacy field for flags michael@0: PLUGIN_REGISTRY_FIELD_DELIMITER, michael@0: tag->IsFromExtension(), michael@0: PLUGIN_REGISTRY_FIELD_DELIMITER, michael@0: PLUGIN_REGISTRY_END_OF_LINE_MARKER); michael@0: michael@0: //description, name & mtypecount are on separate line michael@0: PR_fprintf(fd, "%s%c%c\n%s%c%c\n%d\n", michael@0: (tag->mDescription.get()), michael@0: PLUGIN_REGISTRY_FIELD_DELIMITER, michael@0: PLUGIN_REGISTRY_END_OF_LINE_MARKER, michael@0: (tag->mName.get()), michael@0: PLUGIN_REGISTRY_FIELD_DELIMITER, michael@0: PLUGIN_REGISTRY_END_OF_LINE_MARKER, michael@0: tag->mMimeTypes.Length()); michael@0: michael@0: // Add in each mimetype this plugin supports michael@0: for (uint32_t i = 0; i < tag->mMimeTypes.Length(); i++) { michael@0: PR_fprintf(fd, "%d%c%s%c%s%c%s%c%c\n", michael@0: i,PLUGIN_REGISTRY_FIELD_DELIMITER, michael@0: (tag->mMimeTypes[i].get()), michael@0: PLUGIN_REGISTRY_FIELD_DELIMITER, michael@0: (tag->mMimeDescriptions[i].get()), michael@0: PLUGIN_REGISTRY_FIELD_DELIMITER, michael@0: (tag->mExtensions[i].get()), michael@0: PLUGIN_REGISTRY_FIELD_DELIMITER, michael@0: PLUGIN_REGISTRY_END_OF_LINE_MARKER); michael@0: } michael@0: } michael@0: michael@0: PR_fprintf(fd, "\n[INVALID]\n"); michael@0: michael@0: nsRefPtr invalidPlugins = mInvalidPlugins; michael@0: while (invalidPlugins) { michael@0: // fullPath michael@0: PR_fprintf(fd, "%s%c%c\n", michael@0: (!invalidPlugins->mFullPath.IsEmpty() ? invalidPlugins->mFullPath.get() : ""), michael@0: PLUGIN_REGISTRY_FIELD_DELIMITER, michael@0: PLUGIN_REGISTRY_END_OF_LINE_MARKER); michael@0: michael@0: // lastModifiedTimeStamp michael@0: PR_fprintf(fd, "%lld%c%c\n", michael@0: invalidPlugins->mLastModifiedTime, michael@0: PLUGIN_REGISTRY_FIELD_DELIMITER, michael@0: PLUGIN_REGISTRY_END_OF_LINE_MARKER); michael@0: michael@0: invalidPlugins = invalidPlugins->mNext; michael@0: } michael@0: michael@0: PRStatus prrc; michael@0: prrc = PR_Close(fd); michael@0: if (prrc != PR_SUCCESS) { michael@0: // we should obtain a refined value based on prrc; michael@0: rv = NS_ERROR_FAILURE; michael@0: MOZ_ASSERT(false, "PR_Close() failed."); michael@0: return rv; michael@0: } michael@0: nsCOMPtr parent; michael@0: rv = pluginReg->GetParent(getter_AddRefs(parent)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: rv = pluginReg->MoveToNative(parent, kPluginRegistryFilename); michael@0: return rv; michael@0: } michael@0: michael@0: nsresult michael@0: nsPluginHost::ReadPluginInfo() michael@0: { michael@0: const long PLUGIN_REG_MIMETYPES_ARRAY_SIZE = 12; michael@0: const long PLUGIN_REG_MAX_MIMETYPES = 1000; michael@0: michael@0: // we need to import the legacy flags from the plugin registry once michael@0: const bool pluginStateImported = michael@0: Preferences::GetDefaultBool("plugin.importedState", false); michael@0: michael@0: nsresult rv; michael@0: michael@0: nsCOMPtr directoryService(do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID,&rv)); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: directoryService->Get(NS_APP_USER_PROFILE_50_DIR, NS_GET_IID(nsIFile), michael@0: getter_AddRefs(mPluginRegFile)); michael@0: michael@0: if (!mPluginRegFile) { michael@0: // There is no profile yet, this will tell us if there is going to be one michael@0: // in the future. michael@0: directoryService->Get(NS_APP_PROFILE_DIR_STARTUP, NS_GET_IID(nsIFile), michael@0: getter_AddRefs(mPluginRegFile)); michael@0: if (!mPluginRegFile) michael@0: return NS_ERROR_FAILURE; michael@0: else michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: } michael@0: michael@0: PRFileDesc* fd = nullptr; michael@0: michael@0: nsCOMPtr pluginReg; michael@0: michael@0: rv = mPluginRegFile->Clone(getter_AddRefs(pluginReg)); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: rv = pluginReg->AppendNative(kPluginRegistryFilename); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: int64_t fileSize; michael@0: rv = pluginReg->GetFileSize(&fileSize); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: if (fileSize > INT32_MAX) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: int32_t flen = int32_t(fileSize); michael@0: if (flen == 0) { michael@0: NS_WARNING("Plugins Registry Empty!"); michael@0: return NS_OK; // ERROR CONDITION michael@0: } michael@0: michael@0: nsPluginManifestLineReader reader; michael@0: char* registry = reader.Init(flen); michael@0: if (!registry) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: michael@0: rv = pluginReg->OpenNSPRFileDesc(PR_RDONLY, 0444, &fd); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: // set rv to return an error on goto out michael@0: rv = NS_ERROR_FAILURE; michael@0: michael@0: int32_t bread = PR_Read(fd, registry, flen); michael@0: michael@0: PRStatus prrc; michael@0: prrc = PR_Close(fd); michael@0: if (prrc != PR_SUCCESS) { michael@0: // Strange error: this is one of those "Should not happen" error. michael@0: // we may want to report something more refined than NS_ERROR_FAILURE. michael@0: MOZ_ASSERT(false, "PR_Close() failed."); michael@0: return rv; michael@0: } michael@0: michael@0: if (flen > bread) michael@0: return rv; michael@0: michael@0: if (!ReadSectionHeader(reader, "HEADER")) michael@0: return rv;; michael@0: michael@0: if (!reader.NextLine()) michael@0: return rv; michael@0: michael@0: char* values[6]; michael@0: michael@0: // VersionLiteral, kPluginRegistryVersion michael@0: if (2 != reader.ParseLine(values, 2)) michael@0: return rv; michael@0: michael@0: // VersionLiteral michael@0: if (PL_strcmp(values[0], "Version")) michael@0: return rv; michael@0: michael@0: // kPluginRegistryVersion michael@0: int32_t vdiff = mozilla::CompareVersions(values[1], kPluginRegistryVersion); michael@0: mozilla::Version version(values[1]); michael@0: // If this is a registry from some future version then don't attempt to read it michael@0: if (vdiff > 0) michael@0: return rv; michael@0: // If this is a registry from before the minimum then don't attempt to read it michael@0: if (version < kMinimumRegistryVersion) michael@0: return rv; michael@0: michael@0: // Registry v0.10 and upwards includes the plugin version field michael@0: bool regHasVersion = (version >= "0.10"); michael@0: michael@0: // Registry v0.13 and upwards includes the architecture michael@0: if (version >= "0.13") { michael@0: char* archValues[6]; michael@0: michael@0: if (!reader.NextLine()) { michael@0: return rv; michael@0: } michael@0: michael@0: // ArchLiteral, Architecture michael@0: if (2 != reader.ParseLine(archValues, 2)) { michael@0: return rv; michael@0: } michael@0: michael@0: // ArchLiteral michael@0: if (PL_strcmp(archValues[0], "Arch")) { michael@0: return rv; michael@0: } michael@0: michael@0: nsCOMPtr runtime = do_GetService("@mozilla.org/xre/runtime;1"); michael@0: if (!runtime) { michael@0: return rv; michael@0: } michael@0: michael@0: nsAutoCString arch; michael@0: if (NS_FAILED(runtime->GetXPCOMABI(arch))) { michael@0: return rv; michael@0: } michael@0: michael@0: // If this is a registry from a different architecture then don't attempt to read it michael@0: if (PL_strcmp(archValues[1], arch.get())) { michael@0: return rv; michael@0: } michael@0: } michael@0: michael@0: // Registry v0.13 and upwards includes the list of invalid plugins michael@0: const bool hasInvalidPlugins = (version >= "0.13"); michael@0: michael@0: // Registry v0.16 and upwards always have 0 for their plugin flags, prefs are used instead michael@0: const bool hasValidFlags = (version < "0.16"); michael@0: michael@0: // Registry v0.17 and upwards store whether the plugin comes from an XPI. michael@0: const bool hasFromExtension = (version >= "0.17"); michael@0: michael@0: #if defined(XP_MACOSX) michael@0: const bool hasFullPathInFileNameField = false; michael@0: #else michael@0: const bool hasFullPathInFileNameField = (version < "0.11"); michael@0: #endif michael@0: michael@0: if (!ReadSectionHeader(reader, "PLUGINS")) michael@0: return rv; michael@0: michael@0: while (reader.NextLine()) { michael@0: const char *filename; michael@0: const char *fullpath; michael@0: nsAutoCString derivedFileName; michael@0: michael@0: if (hasInvalidPlugins && *reader.LinePtr() == '[') { michael@0: break; michael@0: } michael@0: michael@0: if (hasFullPathInFileNameField) { michael@0: fullpath = reader.LinePtr(); michael@0: if (!reader.NextLine()) michael@0: return rv; michael@0: // try to derive a file name from the full path michael@0: if (fullpath) { michael@0: nsCOMPtr file = do_CreateInstance("@mozilla.org/file/local;1"); michael@0: file->InitWithNativePath(nsDependentCString(fullpath)); michael@0: file->GetNativeLeafName(derivedFileName); michael@0: filename = derivedFileName.get(); michael@0: } else { michael@0: filename = nullptr; michael@0: } michael@0: michael@0: // skip the next line, useless in this version michael@0: if (!reader.NextLine()) michael@0: return rv; michael@0: } else { michael@0: filename = reader.LinePtr(); michael@0: if (!reader.NextLine()) michael@0: return rv; michael@0: michael@0: fullpath = reader.LinePtr(); michael@0: if (!reader.NextLine()) michael@0: return rv; michael@0: } michael@0: michael@0: const char *version; michael@0: if (regHasVersion) { michael@0: version = reader.LinePtr(); michael@0: if (!reader.NextLine()) michael@0: return rv; michael@0: } else { michael@0: version = "0"; michael@0: } michael@0: michael@0: // lastModifiedTimeStamp|canUnload|tag.mFlag|fromExtension michael@0: const int count = hasFromExtension ? 4 : 3; michael@0: if (reader.ParseLine(values, count) != count) michael@0: return rv; michael@0: michael@0: // If this is an old plugin registry mark this plugin tag to be refreshed michael@0: int64_t lastmod = (vdiff == 0) ? nsCRT::atoll(values[0]) : -1; michael@0: uint32_t tagflag = atoi(values[2]); michael@0: bool fromExtension = false; michael@0: if (hasFromExtension) { michael@0: fromExtension = atoi(values[3]); michael@0: } michael@0: if (!reader.NextLine()) michael@0: return rv; michael@0: michael@0: char *description = reader.LinePtr(); michael@0: if (!reader.NextLine()) michael@0: return rv; michael@0: michael@0: #if MOZ_WIDGET_ANDROID michael@0: // Flash on Android does not populate the version field, but it is tacked on to the description. michael@0: // For example, "Shockwave Flash 11.1 r115" michael@0: if (PL_strncmp("Shockwave Flash ", description, 16) == 0 && description[16]) { michael@0: version = &description[16]; michael@0: } michael@0: #endif michael@0: michael@0: const char *name = reader.LinePtr(); michael@0: if (!reader.NextLine()) michael@0: return rv; michael@0: michael@0: long mimetypecount = std::strtol(reader.LinePtr(), nullptr, 10); michael@0: if (mimetypecount == LONG_MAX || mimetypecount == LONG_MIN || michael@0: mimetypecount >= PLUGIN_REG_MAX_MIMETYPES || mimetypecount < 0) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: char *stackalloced[PLUGIN_REG_MIMETYPES_ARRAY_SIZE * 3]; michael@0: char **mimetypes; michael@0: char **mimedescriptions; michael@0: char **extensions; michael@0: char **heapalloced = 0; michael@0: if (mimetypecount > PLUGIN_REG_MIMETYPES_ARRAY_SIZE - 1) { michael@0: heapalloced = new char *[mimetypecount * 3]; michael@0: mimetypes = heapalloced; michael@0: } else { michael@0: mimetypes = stackalloced; michael@0: } michael@0: mimedescriptions = mimetypes + mimetypecount; michael@0: extensions = mimedescriptions + mimetypecount; michael@0: michael@0: int mtr = 0; //mimetype read michael@0: for (; mtr < mimetypecount; mtr++) { michael@0: if (!reader.NextLine()) michael@0: break; michael@0: michael@0: //line number|mimetype|description|extension michael@0: if (4 != reader.ParseLine(values, 4)) michael@0: break; michael@0: int line = atoi(values[0]); michael@0: if (line != mtr) michael@0: break; michael@0: mimetypes[mtr] = values[1]; michael@0: mimedescriptions[mtr] = values[2]; michael@0: extensions[mtr] = values[3]; michael@0: } michael@0: michael@0: if (mtr != mimetypecount) { michael@0: if (heapalloced) { michael@0: delete [] heapalloced; michael@0: } michael@0: return rv; michael@0: } michael@0: michael@0: nsRefPtr tag = new nsPluginTag(name, michael@0: description, michael@0: filename, michael@0: fullpath, michael@0: version, michael@0: (const char* const*)mimetypes, michael@0: (const char* const*)mimedescriptions, michael@0: (const char* const*)extensions, michael@0: mimetypecount, lastmod, fromExtension, true); michael@0: if (heapalloced) michael@0: delete [] heapalloced; michael@0: michael@0: // Import flags from registry into prefs for old registry versions michael@0: if (hasValidFlags && !pluginStateImported) { michael@0: tag->ImportFlagsToPrefs(tagflag); michael@0: } michael@0: michael@0: PR_LOG(nsPluginLogging::gPluginLog, PLUGIN_LOG_BASIC, michael@0: ("LoadCachedPluginsInfo : Loading Cached plugininfo for %s\n", tag->mFileName.get())); michael@0: tag->mNext = mCachedPlugins; michael@0: mCachedPlugins = tag; michael@0: } michael@0: michael@0: // On Android we always want to try to load a plugin again (Flash). Bug 935676. michael@0: #ifndef MOZ_WIDGET_ANDROID michael@0: if (hasInvalidPlugins) { michael@0: if (!ReadSectionHeader(reader, "INVALID")) { michael@0: return rv; michael@0: } michael@0: michael@0: while (reader.NextLine()) { michael@0: const char *fullpath = reader.LinePtr(); michael@0: if (!reader.NextLine()) { michael@0: return rv; michael@0: } michael@0: michael@0: const char *lastModifiedTimeStamp = reader.LinePtr(); michael@0: int64_t lastmod = (vdiff == 0) ? nsCRT::atoll(lastModifiedTimeStamp) : -1; michael@0: michael@0: nsRefPtr invalidTag = new nsInvalidPluginTag(fullpath, lastmod); michael@0: michael@0: invalidTag->mNext = mInvalidPlugins; michael@0: if (mInvalidPlugins) { michael@0: mInvalidPlugins->mPrev = invalidTag; michael@0: } michael@0: mInvalidPlugins = invalidTag; michael@0: } michael@0: } michael@0: #endif michael@0: michael@0: // flip the pref so we don't import the legacy flags again michael@0: Preferences::SetBool("plugin.importedState", true); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: nsPluginHost::RemoveCachedPluginsInfo(const char *filePath, nsPluginTag **result) michael@0: { michael@0: nsRefPtr prev; michael@0: nsRefPtr tag = mCachedPlugins; michael@0: while (tag) michael@0: { michael@0: if (tag->mFullPath.Equals(filePath)) { michael@0: // Found it. Remove it from our list michael@0: if (prev) michael@0: prev->mNext = tag->mNext; michael@0: else michael@0: mCachedPlugins = tag->mNext; michael@0: tag->mNext = nullptr; michael@0: *result = tag; michael@0: NS_ADDREF(*result); michael@0: break; michael@0: } michael@0: prev = tag; michael@0: tag = tag->mNext; michael@0: } michael@0: } michael@0: michael@0: #ifdef XP_WIN michael@0: nsresult michael@0: nsPluginHost::EnsurePrivateDirServiceProvider() michael@0: { michael@0: if (!mPrivateDirServiceProvider) { michael@0: nsresult rv; michael@0: mPrivateDirServiceProvider = new nsPluginDirServiceProvider(); michael@0: if (!mPrivateDirServiceProvider) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: nsCOMPtr dirService(do_GetService(kDirectoryServiceContractID, &rv)); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: rv = dirService->RegisterProvider(mPrivateDirServiceProvider); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: } michael@0: return NS_OK; michael@0: } michael@0: #endif /* XP_WIN */ michael@0: michael@0: nsresult nsPluginHost::NewPluginURLStream(const nsString& aURL, michael@0: nsNPAPIPluginInstance *aInstance, michael@0: nsNPAPIPluginStreamListener* aListener, michael@0: nsIInputStream *aPostStream, michael@0: const char *aHeadersData, michael@0: uint32_t aHeadersDataLen) michael@0: { michael@0: nsCOMPtr url; michael@0: nsAutoString absUrl; michael@0: nsresult rv; michael@0: michael@0: if (aURL.Length() <= 0) michael@0: return NS_OK; michael@0: michael@0: // get the base URI for the plugin to create an absolute url michael@0: // in case aURL is relative michael@0: nsRefPtr owner = aInstance->GetOwner(); michael@0: if (owner) { michael@0: rv = NS_MakeAbsoluteURI(absUrl, aURL, nsCOMPtr(owner->GetBaseURI())); michael@0: } michael@0: michael@0: if (absUrl.IsEmpty()) michael@0: absUrl.Assign(aURL); michael@0: michael@0: rv = NS_NewURI(getter_AddRefs(url), absUrl); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: nsCOMPtr element; michael@0: nsCOMPtr doc; michael@0: if (owner) { michael@0: owner->GetDOMElement(getter_AddRefs(element)); michael@0: owner->GetDocument(getter_AddRefs(doc)); michael@0: } michael@0: michael@0: int16_t shouldLoad = nsIContentPolicy::ACCEPT; michael@0: rv = NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_OBJECT_SUBREQUEST, michael@0: url, michael@0: (doc ? doc->NodePrincipal() : nullptr), michael@0: element, michael@0: EmptyCString(), //mime guess michael@0: nullptr, //extra michael@0: &shouldLoad); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: if (NS_CP_REJECTED(shouldLoad)) { michael@0: // Disallowed by content policy michael@0: return NS_ERROR_CONTENT_BLOCKED; michael@0: } michael@0: michael@0: nsRefPtr listenerPeer = new nsPluginStreamListenerPeer(); michael@0: if (!listenerPeer) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: michael@0: rv = listenerPeer->Initialize(url, aInstance, aListener); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: nsCOMPtr channel; michael@0: rv = NS_NewChannel(getter_AddRefs(channel), url, nullptr, michael@0: nullptr, /* do not add this internal plugin's channel michael@0: on the load group otherwise this channel could be canceled michael@0: form |nsDocShell::OnLinkClickSync| bug 166613 */ michael@0: listenerPeer); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: if (doc) { michael@0: // Set the owner of channel to the document principal... michael@0: channel->SetOwner(doc->NodePrincipal()); michael@0: michael@0: // And if it's a script allow it to execute against the michael@0: // document's script context. michael@0: nsCOMPtr scriptChannel(do_QueryInterface(channel)); michael@0: if (scriptChannel) { michael@0: scriptChannel->SetExecutionPolicy(nsIScriptChannel::EXECUTE_NORMAL); michael@0: // Plug-ins seem to depend on javascript: URIs running synchronously michael@0: scriptChannel->SetExecuteAsync(false); michael@0: } michael@0: } michael@0: michael@0: // deal with headers and post data michael@0: nsCOMPtr httpChannel(do_QueryInterface(channel)); michael@0: if (httpChannel) { michael@0: if (!aPostStream) { michael@0: // Only set the Referer header for GET requests because IIS throws michael@0: // errors about malformed requests if we include it in POSTs. See michael@0: // bug 724465. michael@0: nsCOMPtr referer; michael@0: michael@0: nsCOMPtr olc = do_QueryInterface(element); michael@0: if (olc) michael@0: olc->GetSrcURI(getter_AddRefs(referer)); michael@0: michael@0: michael@0: if (!referer) { michael@0: if (!doc) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: referer = doc->GetDocumentURI(); michael@0: } michael@0: michael@0: rv = httpChannel->SetReferrer(referer); michael@0: NS_ENSURE_SUCCESS(rv,rv); michael@0: } michael@0: michael@0: if (aPostStream) { michael@0: // XXX it's a bit of a hack to rewind the postdata stream michael@0: // here but it has to be done in case the post data is michael@0: // being reused multiple times. michael@0: nsCOMPtr michael@0: postDataSeekable(do_QueryInterface(aPostStream)); michael@0: if (postDataSeekable) michael@0: postDataSeekable->Seek(nsISeekableStream::NS_SEEK_SET, 0); michael@0: michael@0: nsCOMPtr uploadChannel(do_QueryInterface(httpChannel)); michael@0: NS_ASSERTION(uploadChannel, "http must support nsIUploadChannel"); michael@0: michael@0: uploadChannel->SetUploadStream(aPostStream, EmptyCString(), -1); michael@0: } michael@0: michael@0: if (aHeadersData) { michael@0: rv = AddHeadersToChannel(aHeadersData, aHeadersDataLen, httpChannel); michael@0: NS_ENSURE_SUCCESS(rv,rv); michael@0: } michael@0: } michael@0: rv = channel->AsyncOpen(listenerPeer, nullptr); michael@0: if (NS_SUCCEEDED(rv)) michael@0: listenerPeer->TrackRequest(channel); michael@0: return rv; michael@0: } michael@0: michael@0: // Called by GetURL and PostURL michael@0: nsresult michael@0: nsPluginHost::DoURLLoadSecurityCheck(nsNPAPIPluginInstance *aInstance, michael@0: const char* aURL) michael@0: { michael@0: if (!aURL || *aURL == '\0') michael@0: return NS_OK; michael@0: michael@0: // get the base URI for the plugin element michael@0: nsRefPtr owner = aInstance->GetOwner(); michael@0: if (!owner) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: nsCOMPtr baseURI = owner->GetBaseURI(); michael@0: if (!baseURI) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: // Create an absolute URL for the target in case the target is relative michael@0: nsCOMPtr targetURL; michael@0: NS_NewURI(getter_AddRefs(targetURL), aURL, baseURI); michael@0: if (!targetURL) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: nsCOMPtr doc; michael@0: owner->GetDocument(getter_AddRefs(doc)); michael@0: if (!doc) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: nsresult rv; michael@0: nsCOMPtr secMan( michael@0: do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv)); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: return secMan->CheckLoadURIWithPrincipal(doc->NodePrincipal(), targetURL, michael@0: nsIScriptSecurityManager::STANDARD); michael@0: michael@0: } michael@0: michael@0: nsresult michael@0: nsPluginHost::AddHeadersToChannel(const char *aHeadersData, michael@0: uint32_t aHeadersDataLen, michael@0: nsIChannel *aGenericChannel) michael@0: { michael@0: nsresult rv = NS_OK; michael@0: michael@0: nsCOMPtr aChannel = do_QueryInterface(aGenericChannel); michael@0: if (!aChannel) { michael@0: return NS_ERROR_NULL_POINTER; michael@0: } michael@0: michael@0: // used during the manipulation of the String from the aHeadersData michael@0: nsAutoCString headersString; michael@0: nsAutoCString oneHeader; michael@0: nsAutoCString headerName; michael@0: nsAutoCString headerValue; michael@0: int32_t crlf = 0; michael@0: int32_t colon = 0; michael@0: michael@0: // Turn the char * buffer into an nsString. michael@0: headersString = aHeadersData; michael@0: michael@0: // Iterate over the nsString: for each "\r\n" delimited chunk, michael@0: // add the value as a header to the nsIHTTPChannel michael@0: while (true) { michael@0: crlf = headersString.Find("\r\n", true); michael@0: if (-1 == crlf) { michael@0: rv = NS_OK; michael@0: return rv; michael@0: } michael@0: headersString.Mid(oneHeader, 0, crlf); michael@0: headersString.Cut(0, crlf + 2); michael@0: oneHeader.StripWhitespace(); michael@0: colon = oneHeader.Find(":"); michael@0: if (-1 == colon) { michael@0: rv = NS_ERROR_NULL_POINTER; michael@0: return rv; michael@0: } michael@0: oneHeader.Left(headerName, colon); michael@0: colon++; michael@0: oneHeader.Mid(headerValue, colon, oneHeader.Length() - colon); michael@0: michael@0: // FINALLY: we can set the header! michael@0: michael@0: rv = aChannel->SetRequestHeader(headerName, headerValue, true); michael@0: if (NS_FAILED(rv)) { michael@0: rv = NS_ERROR_NULL_POINTER; michael@0: return rv; michael@0: } michael@0: } michael@0: return rv; michael@0: } michael@0: michael@0: nsresult michael@0: nsPluginHost::StopPluginInstance(nsNPAPIPluginInstance* aInstance) michael@0: { michael@0: if (PluginDestructionGuard::DelayDestroy(aInstance)) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: PLUGIN_LOG(PLUGIN_LOG_NORMAL, michael@0: ("nsPluginHost::StopPluginInstance called instance=%p\n",aInstance)); michael@0: michael@0: if (aInstance->HasStartedDestroying()) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: Telemetry::AutoTimer timer; michael@0: aInstance->Stop(); michael@0: michael@0: // if the instance does not want to be 'cached' just remove it michael@0: bool doCache = aInstance->ShouldCache(); michael@0: if (doCache) { michael@0: // try to get the max cached instances from a pref or use default michael@0: uint32_t cachedInstanceLimit = michael@0: Preferences::GetUint(NS_PREF_MAX_NUM_CACHED_INSTANCES, michael@0: DEFAULT_NUMBER_OF_STOPPED_INSTANCES); michael@0: if (StoppedInstanceCount() >= cachedInstanceLimit) { michael@0: nsNPAPIPluginInstance *oldestInstance = FindOldestStoppedInstance(); michael@0: if (oldestInstance) { michael@0: nsPluginTag* pluginTag = TagForPlugin(oldestInstance->GetPlugin()); michael@0: oldestInstance->Destroy(); michael@0: mInstances.RemoveElement(oldestInstance); michael@0: // TODO: Remove this check once bug 752422 was investigated michael@0: if (pluginTag) { michael@0: OnPluginInstanceDestroyed(pluginTag); michael@0: } michael@0: } michael@0: } michael@0: } else { michael@0: nsPluginTag* pluginTag = TagForPlugin(aInstance->GetPlugin()); michael@0: aInstance->Destroy(); michael@0: mInstances.RemoveElement(aInstance); michael@0: // TODO: Remove this check once bug 752422 was investigated michael@0: if (pluginTag) { michael@0: OnPluginInstanceDestroyed(pluginTag); michael@0: } michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult nsPluginHost::NewPluginStreamListener(nsIURI* aURI, michael@0: nsNPAPIPluginInstance* aInstance, michael@0: nsIStreamListener **aStreamListener) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aURI); michael@0: NS_ENSURE_ARG_POINTER(aStreamListener); michael@0: michael@0: nsRefPtr listener = new nsPluginStreamListenerPeer(); michael@0: nsresult rv = listener->Initialize(aURI, aInstance, nullptr); michael@0: if (NS_FAILED(rv)) { michael@0: return rv; michael@0: } michael@0: michael@0: listener.forget(aStreamListener); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP nsPluginHost::Observe(nsISupports *aSubject, michael@0: const char *aTopic, michael@0: const char16_t *someData) michael@0: { michael@0: if (!strcmp(NS_XPCOM_SHUTDOWN_OBSERVER_ID, aTopic)) { michael@0: OnShutdown(); michael@0: UnloadPlugins(); michael@0: sInst->Release(); michael@0: } michael@0: if (!strcmp(NS_PREFBRANCH_PREFCHANGE_TOPIC_ID, aTopic)) { michael@0: mPluginsDisabled = Preferences::GetBool("plugin.disable", false); michael@0: mPluginsClickToPlay = Preferences::GetBool("plugins.click_to_play", false); michael@0: // Unload or load plugins as needed michael@0: if (mPluginsDisabled) { michael@0: UnloadPlugins(); michael@0: } else { michael@0: LoadPlugins(); michael@0: } michael@0: } michael@0: if (!strcmp("blocklist-updated", aTopic)) { michael@0: nsPluginTag* plugin = mPlugins; michael@0: while (plugin) { michael@0: plugin->InvalidateBlocklistState(); michael@0: plugin = plugin->mNext; michael@0: } michael@0: } michael@0: #ifdef MOZ_WIDGET_ANDROID michael@0: if (!strcmp("application-background", aTopic)) { michael@0: for(uint32_t i = 0; i < mInstances.Length(); i++) { michael@0: mInstances[i]->NotifyForeground(false); michael@0: } michael@0: } michael@0: if (!strcmp("application-foreground", aTopic)) { michael@0: for(uint32_t i = 0; i < mInstances.Length(); i++) { michael@0: if (mInstances[i]->IsOnScreen()) michael@0: mInstances[i]->NotifyForeground(true); michael@0: } michael@0: } michael@0: if (!strcmp("memory-pressure", aTopic)) { michael@0: for(uint32_t i = 0; i < mInstances.Length(); i++) { michael@0: mInstances[i]->MemoryPressure(); michael@0: } michael@0: } michael@0: #endif michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsPluginHost::ParsePostBufferToFixHeaders(const char *inPostData, uint32_t inPostDataLen, michael@0: char **outPostData, uint32_t *outPostDataLen) michael@0: { michael@0: if (!inPostData || !outPostData || !outPostDataLen) michael@0: return NS_ERROR_NULL_POINTER; michael@0: michael@0: *outPostData = 0; michael@0: *outPostDataLen = 0; michael@0: michael@0: const char CR = '\r'; michael@0: const char LF = '\n'; michael@0: const char CRLFCRLF[] = {CR,LF,CR,LF,'\0'}; // C string"\r\n\r\n" michael@0: const char ContentLenHeader[] = "Content-length"; michael@0: michael@0: nsAutoTArray singleLF; michael@0: const char *pSCntlh = 0;// pointer to start of ContentLenHeader in inPostData michael@0: const char *pSod = 0; // pointer to start of data in inPostData michael@0: const char *pEoh = 0; // pointer to end of headers in inPostData michael@0: const char *pEod = inPostData + inPostDataLen; // pointer to end of inPostData michael@0: if (*inPostData == LF) { michael@0: // If no custom headers are required, simply add a blank michael@0: // line ('\n') to the beginning of the file or buffer. michael@0: // so *inPostData == '\n' is valid michael@0: pSod = inPostData + 1; michael@0: } else { michael@0: const char *s = inPostData; //tmp pointer to sourse inPostData michael@0: while (s < pEod) { michael@0: if (!pSCntlh && michael@0: (*s == 'C' || *s == 'c') && michael@0: (s + sizeof(ContentLenHeader) - 1 < pEod) && michael@0: (!PL_strncasecmp(s, ContentLenHeader, sizeof(ContentLenHeader) - 1))) michael@0: { michael@0: // lets assume this is ContentLenHeader for now michael@0: const char *p = pSCntlh = s; michael@0: p += sizeof(ContentLenHeader) - 1; michael@0: // search for first CR or LF == end of ContentLenHeader michael@0: for (; p < pEod; p++) { michael@0: if (*p == CR || *p == LF) { michael@0: // got delimiter, michael@0: // one more check; if previous char is a digit michael@0: // most likely pSCntlh points to the start of ContentLenHeader michael@0: if (*(p-1) >= '0' && *(p-1) <= '9') { michael@0: s = p; michael@0: } michael@0: break; //for loop michael@0: } michael@0: } michael@0: if (pSCntlh == s) { // curret ptr is the same michael@0: pSCntlh = 0; // that was not ContentLenHeader michael@0: break; // there is nothing to parse, break *WHILE LOOP* here michael@0: } michael@0: } michael@0: michael@0: if (*s == CR) { michael@0: if (pSCntlh && // only if ContentLenHeader is found we are looking for end of headers michael@0: ((s + sizeof(CRLFCRLF)-1) <= pEod) && michael@0: !memcmp(s, CRLFCRLF, sizeof(CRLFCRLF)-1)) michael@0: { michael@0: s += sizeof(CRLFCRLF)-1; michael@0: pEoh = pSod = s; // data stars here michael@0: break; michael@0: } michael@0: } else if (*s == LF) { michael@0: if (*(s-1) != CR) { michael@0: singleLF.AppendElement(s); michael@0: } michael@0: if (pSCntlh && (s+1 < pEod) && (*(s+1) == LF)) { michael@0: s++; michael@0: singleLF.AppendElement(s); michael@0: s++; michael@0: pEoh = pSod = s; // data stars here michael@0: break; michael@0: } michael@0: } michael@0: s++; michael@0: } michael@0: } michael@0: michael@0: // deal with output buffer michael@0: if (!pSod) { // lets assume whole buffer is a data michael@0: pSod = inPostData; michael@0: } michael@0: michael@0: uint32_t newBufferLen = 0; michael@0: uint32_t dataLen = pEod - pSod; michael@0: uint32_t headersLen = pEoh ? pSod - inPostData : 0; michael@0: michael@0: char *p; // tmp ptr into new output buf michael@0: if (headersLen) { // we got a headers michael@0: // this function does not make any assumption on correctness michael@0: // of ContentLenHeader value in this case. michael@0: michael@0: newBufferLen = dataLen + headersLen; michael@0: // in case there were single LFs in headers michael@0: // reserve an extra space for CR will be added before each single LF michael@0: int cntSingleLF = singleLF.Length(); michael@0: newBufferLen += cntSingleLF; michael@0: michael@0: if (!(*outPostData = p = (char*)nsMemory::Alloc(newBufferLen))) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: michael@0: // deal with single LF michael@0: const char *s = inPostData; michael@0: if (cntSingleLF) { michael@0: for (int i=0; i inFile; michael@0: rv = NS_GetFileFromURLSpec(nsDependentCString(aPostDataURL), michael@0: getter_AddRefs(inFile)); michael@0: if (NS_FAILED(rv)) { michael@0: nsCOMPtr localFile; michael@0: rv = NS_NewNativeLocalFile(nsDependentCString(aPostDataURL), false, michael@0: getter_AddRefs(localFile)); michael@0: if (NS_FAILED(rv)) return rv; michael@0: inFile = localFile; michael@0: } michael@0: rv = inFile->GetFileSize(&fileSize); michael@0: if (NS_FAILED(rv)) return rv; michael@0: rv = inFile->GetNativePath(filename); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: if (fileSize != 0) { michael@0: nsCOMPtr inStream; michael@0: rv = NS_NewLocalFileInputStream(getter_AddRefs(inStream), inFile); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: // Create a temporary file to write the http Content-length: michael@0: // %ld\r\n\" header and "\r\n" == end of headers for post data to michael@0: michael@0: nsCOMPtr tempFile; michael@0: rv = GetPluginTempDir(getter_AddRefs(tempFile)); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: nsAutoCString inFileName; michael@0: inFile->GetNativeLeafName(inFileName); michael@0: // XXX hack around bug 70083 michael@0: inFileName.Insert(NS_LITERAL_CSTRING("post-"), 0); michael@0: rv = tempFile->AppendNative(inFileName); michael@0: michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: // make it unique, and mode == 0600, not world-readable michael@0: rv = tempFile->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0600); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: nsCOMPtr outStream; michael@0: if (NS_SUCCEEDED(rv)) { michael@0: rv = NS_NewLocalFileOutputStream(getter_AddRefs(outStream), michael@0: tempFile, michael@0: (PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE), michael@0: 0600); // 600 so others can't read our form data michael@0: } michael@0: NS_ASSERTION(NS_SUCCEEDED(rv), "Post data file couldn't be created!"); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: char buf[1024]; michael@0: uint32_t br, bw; michael@0: bool firstRead = true; michael@0: while (1) { michael@0: // Read() mallocs if buffer is null michael@0: rv = inStream->Read(buf, 1024, &br); michael@0: if (NS_FAILED(rv) || (int32_t)br <= 0) michael@0: break; michael@0: if (firstRead) { michael@0: //"For protocols in which the headers must be distinguished from the body, michael@0: // such as HTTP, the buffer or file should contain the headers, followed by michael@0: // a blank line, then the body. If no custom headers are required, simply michael@0: // add a blank line ('\n') to the beginning of the file or buffer. michael@0: michael@0: char *parsedBuf; michael@0: // assuming first 1K (or what we got) has all headers in, michael@0: // lets parse it through nsPluginHost::ParsePostBufferToFixHeaders() michael@0: ParsePostBufferToFixHeaders((const char *)buf, br, &parsedBuf, &bw); michael@0: rv = outStream->Write(parsedBuf, bw, &br); michael@0: nsMemory::Free(parsedBuf); michael@0: if (NS_FAILED(rv) || (bw != br)) michael@0: break; michael@0: michael@0: firstRead = false; michael@0: continue; michael@0: } michael@0: bw = br; michael@0: rv = outStream->Write(buf, bw, &br); michael@0: if (NS_FAILED(rv) || (bw != br)) michael@0: break; michael@0: } michael@0: michael@0: inStream->Close(); michael@0: outStream->Close(); michael@0: if (NS_SUCCEEDED(rv)) michael@0: tempFile.forget(aTmpFile); michael@0: } michael@0: return rv; michael@0: } michael@0: michael@0: nsresult michael@0: nsPluginHost::NewPluginNativeWindow(nsPluginNativeWindow ** aPluginNativeWindow) michael@0: { michael@0: return PLUG_NewPluginNativeWindow(aPluginNativeWindow); michael@0: } michael@0: michael@0: nsresult michael@0: nsPluginHost::GetPluginName(nsNPAPIPluginInstance *aPluginInstance, michael@0: const char** aPluginName) michael@0: { michael@0: nsNPAPIPluginInstance *instance = static_cast(aPluginInstance); michael@0: if (!instance) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: nsNPAPIPlugin* plugin = instance->GetPlugin(); michael@0: if (!plugin) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: *aPluginName = TagForPlugin(plugin)->mName.get(); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsPluginHost::GetPluginTagForInstance(nsNPAPIPluginInstance *aPluginInstance, michael@0: nsIPluginTag **aPluginTag) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aPluginInstance); michael@0: NS_ENSURE_ARG_POINTER(aPluginTag); michael@0: michael@0: nsNPAPIPlugin *plugin = aPluginInstance->GetPlugin(); michael@0: if (!plugin) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: *aPluginTag = TagForPlugin(plugin); michael@0: michael@0: NS_ADDREF(*aPluginTag); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP nsPluginHost::Notify(nsITimer* timer) michael@0: { michael@0: nsRefPtr pluginTag = mPlugins; michael@0: while (pluginTag) { michael@0: if (pluginTag->mUnloadTimer == timer) { michael@0: if (!IsRunningPlugin(pluginTag)) { michael@0: pluginTag->TryUnloadPlugin(false); michael@0: } michael@0: return NS_OK; michael@0: } michael@0: pluginTag = pluginTag->mNext; michael@0: } michael@0: michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: #ifdef XP_WIN michael@0: // Re-enable any top level browser windows that were disabled by modal dialogs michael@0: // displayed by the crashed plugin. michael@0: static void michael@0: CheckForDisabledWindows() michael@0: { michael@0: nsCOMPtr wm(do_GetService(NS_WINDOWMEDIATOR_CONTRACTID)); michael@0: if (!wm) michael@0: return; michael@0: michael@0: nsCOMPtr windowList; michael@0: wm->GetXULWindowEnumerator(nullptr, getter_AddRefs(windowList)); michael@0: if (!windowList) michael@0: return; michael@0: michael@0: bool haveWindows; michael@0: do { michael@0: windowList->HasMoreElements(&haveWindows); michael@0: if (!haveWindows) michael@0: return; michael@0: michael@0: nsCOMPtr supportsWindow; michael@0: windowList->GetNext(getter_AddRefs(supportsWindow)); michael@0: nsCOMPtr baseWin(do_QueryInterface(supportsWindow)); michael@0: if (baseWin) { michael@0: nsCOMPtr widget; michael@0: baseWin->GetMainWidget(getter_AddRefs(widget)); michael@0: if (widget && !widget->GetParent() && michael@0: widget->IsVisible() && michael@0: !widget->IsEnabled()) { michael@0: nsIWidget* child = widget->GetFirstChild(); michael@0: bool enable = true; michael@0: while (child) { michael@0: if (child->WindowType() == eWindowType_dialog) { michael@0: enable = false; michael@0: break; michael@0: } michael@0: child = child->GetNextSibling(); michael@0: } michael@0: if (enable) { michael@0: widget->Enable(true); michael@0: } michael@0: } michael@0: } michael@0: } while (haveWindows); michael@0: } michael@0: #endif michael@0: michael@0: void michael@0: nsPluginHost::PluginCrashed(nsNPAPIPlugin* aPlugin, michael@0: const nsAString& pluginDumpID, michael@0: const nsAString& browserDumpID) michael@0: { michael@0: nsPluginTag* crashedPluginTag = TagForPlugin(aPlugin); michael@0: michael@0: // Notify the app's observer that a plugin crashed so it can submit michael@0: // a crashreport. michael@0: bool submittedCrashReport = false; michael@0: nsCOMPtr obsService = michael@0: mozilla::services::GetObserverService(); michael@0: nsCOMPtr propbag = michael@0: do_CreateInstance("@mozilla.org/hash-property-bag;1"); michael@0: if (obsService && propbag) { michael@0: propbag->SetPropertyAsAString(NS_LITERAL_STRING("pluginDumpID"), michael@0: pluginDumpID); michael@0: propbag->SetPropertyAsAString(NS_LITERAL_STRING("browserDumpID"), michael@0: browserDumpID); michael@0: propbag->SetPropertyAsBool(NS_LITERAL_STRING("submittedCrashReport"), michael@0: submittedCrashReport); michael@0: obsService->NotifyObservers(propbag, "plugin-crashed", nullptr); michael@0: // see if an observer submitted a crash report. michael@0: propbag->GetPropertyAsBool(NS_LITERAL_STRING("submittedCrashReport"), michael@0: &submittedCrashReport); michael@0: } michael@0: michael@0: // Invalidate each nsPluginInstanceTag for the crashed plugin michael@0: michael@0: for (uint32_t i = mInstances.Length(); i > 0; i--) { michael@0: nsNPAPIPluginInstance* instance = mInstances[i - 1]; michael@0: if (instance->GetPlugin() == aPlugin) { michael@0: // notify the content node (nsIObjectLoadingContent) that the michael@0: // plugin has crashed michael@0: nsCOMPtr domElement; michael@0: instance->GetDOMElement(getter_AddRefs(domElement)); michael@0: nsCOMPtr objectContent(do_QueryInterface(domElement)); michael@0: if (objectContent) { michael@0: objectContent->PluginCrashed(crashedPluginTag, pluginDumpID, browserDumpID, michael@0: submittedCrashReport); michael@0: } michael@0: michael@0: instance->Destroy(); michael@0: mInstances.RemoveElement(instance); michael@0: OnPluginInstanceDestroyed(crashedPluginTag); michael@0: } michael@0: } michael@0: michael@0: // Only after all instances have been invalidated is it safe to null michael@0: // out nsPluginTag.mPlugin. The next time we try to create an michael@0: // instance of this plugin we reload it (launch a new plugin process). michael@0: michael@0: crashedPluginTag->mPlugin = nullptr; michael@0: michael@0: #ifdef XP_WIN michael@0: CheckForDisabledWindows(); michael@0: #endif michael@0: } michael@0: michael@0: nsNPAPIPluginInstance* michael@0: nsPluginHost::FindInstance(const char *mimetype) michael@0: { michael@0: for (uint32_t i = 0; i < mInstances.Length(); i++) { michael@0: nsNPAPIPluginInstance* instance = mInstances[i]; michael@0: michael@0: const char* mt; michael@0: nsresult rv = instance->GetMIMEType(&mt); michael@0: if (NS_FAILED(rv)) michael@0: continue; michael@0: michael@0: if (PL_strcasecmp(mt, mimetype) == 0) michael@0: return instance; michael@0: } michael@0: michael@0: return nullptr; michael@0: } michael@0: michael@0: nsNPAPIPluginInstance* michael@0: nsPluginHost::FindOldestStoppedInstance() michael@0: { michael@0: nsNPAPIPluginInstance *oldestInstance = nullptr; michael@0: TimeStamp oldestTime = TimeStamp::Now(); michael@0: for (uint32_t i = 0; i < mInstances.Length(); i++) { michael@0: nsNPAPIPluginInstance *instance = mInstances[i]; michael@0: if (instance->IsRunning()) michael@0: continue; michael@0: michael@0: TimeStamp time = instance->StopTime(); michael@0: if (time < oldestTime) { michael@0: oldestTime = time; michael@0: oldestInstance = instance; michael@0: } michael@0: } michael@0: michael@0: return oldestInstance; michael@0: } michael@0: michael@0: uint32_t michael@0: nsPluginHost::StoppedInstanceCount() michael@0: { michael@0: uint32_t stoppedCount = 0; michael@0: for (uint32_t i = 0; i < mInstances.Length(); i++) { michael@0: nsNPAPIPluginInstance *instance = mInstances[i]; michael@0: if (!instance->IsRunning()) michael@0: stoppedCount++; michael@0: } michael@0: return stoppedCount; michael@0: } michael@0: michael@0: nsTArray< nsRefPtr >* michael@0: nsPluginHost::InstanceArray() michael@0: { michael@0: return &mInstances; michael@0: } michael@0: michael@0: void michael@0: nsPluginHost::DestroyRunningInstances(nsPluginTag* aPluginTag) michael@0: { michael@0: for (int32_t i = mInstances.Length(); i > 0; i--) { michael@0: nsNPAPIPluginInstance *instance = mInstances[i - 1]; michael@0: if (instance->IsRunning() && (!aPluginTag || aPluginTag == TagForPlugin(instance->GetPlugin()))) { michael@0: instance->SetWindow(nullptr); michael@0: instance->Stop(); michael@0: michael@0: // Get rid of all the instances without the possibility of caching. michael@0: nsPluginTag* pluginTag = TagForPlugin(instance->GetPlugin()); michael@0: instance->SetWindow(nullptr); michael@0: michael@0: nsCOMPtr domElement; michael@0: instance->GetDOMElement(getter_AddRefs(domElement)); michael@0: nsCOMPtr objectContent = michael@0: do_QueryInterface(domElement); michael@0: michael@0: instance->Destroy(); michael@0: michael@0: mInstances.RemoveElement(instance); michael@0: OnPluginInstanceDestroyed(pluginTag); michael@0: michael@0: // Notify owning content that we destroyed its plugin out from under it michael@0: if (objectContent) { michael@0: objectContent->PluginDestroyed(); michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: // Runnable that does an async destroy of a plugin. michael@0: michael@0: class nsPluginDestroyRunnable : public nsRunnable, michael@0: public PRCList michael@0: { michael@0: public: michael@0: nsPluginDestroyRunnable(nsNPAPIPluginInstance *aInstance) michael@0: : mInstance(aInstance) michael@0: { michael@0: PR_INIT_CLIST(this); michael@0: PR_APPEND_LINK(this, &sRunnableListHead); michael@0: } michael@0: michael@0: virtual ~nsPluginDestroyRunnable() michael@0: { michael@0: PR_REMOVE_LINK(this); michael@0: } michael@0: michael@0: NS_IMETHOD Run() michael@0: { michael@0: nsRefPtr instance; michael@0: michael@0: // Null out mInstance to make sure this code in another runnable michael@0: // will do the right thing even if someone was holding on to this michael@0: // runnable longer than we expect. michael@0: instance.swap(mInstance); michael@0: michael@0: if (PluginDestructionGuard::DelayDestroy(instance)) { michael@0: // It's still not safe to destroy the plugin, it's now up to the michael@0: // outermost guard on the stack to take care of the destruction. michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsPluginDestroyRunnable *r = michael@0: static_cast(PR_NEXT_LINK(&sRunnableListHead)); michael@0: michael@0: while (r != &sRunnableListHead) { michael@0: if (r != this && r->mInstance == instance) { michael@0: // There's another runnable scheduled to tear down michael@0: // instance. Let it do the job. michael@0: return NS_OK; michael@0: } michael@0: r = static_cast(PR_NEXT_LINK(r)); michael@0: } michael@0: michael@0: PLUGIN_LOG(PLUGIN_LOG_NORMAL, michael@0: ("Doing delayed destroy of instance %p\n", instance.get())); michael@0: michael@0: nsRefPtr host = nsPluginHost::GetInst(); michael@0: if (host) michael@0: host->StopPluginInstance(instance); michael@0: michael@0: PLUGIN_LOG(PLUGIN_LOG_NORMAL, michael@0: ("Done with delayed destroy of instance %p\n", instance.get())); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: protected: michael@0: nsRefPtr mInstance; michael@0: michael@0: static PRCList sRunnableListHead; michael@0: }; michael@0: michael@0: PRCList nsPluginDestroyRunnable::sRunnableListHead = michael@0: PR_INIT_STATIC_CLIST(&nsPluginDestroyRunnable::sRunnableListHead); michael@0: michael@0: PRCList PluginDestructionGuard::sListHead = michael@0: PR_INIT_STATIC_CLIST(&PluginDestructionGuard::sListHead); michael@0: michael@0: PluginDestructionGuard::PluginDestructionGuard(nsNPAPIPluginInstance *aInstance) michael@0: : mInstance(aInstance) michael@0: { michael@0: Init(); michael@0: } michael@0: michael@0: PluginDestructionGuard::PluginDestructionGuard(NPP npp) michael@0: : mInstance(npp ? static_cast(npp->ndata) : nullptr) michael@0: { michael@0: Init(); michael@0: } michael@0: michael@0: PluginDestructionGuard::~PluginDestructionGuard() michael@0: { michael@0: NS_ASSERTION(NS_IsMainThread(), "Should be on the main thread"); michael@0: michael@0: PR_REMOVE_LINK(this); michael@0: michael@0: if (mDelayedDestroy) { michael@0: // We've attempted to destroy the plugin instance we're holding on michael@0: // to while we were guarding it. Do the actual destroy now, off of michael@0: // a runnable. michael@0: nsRefPtr evt = michael@0: new nsPluginDestroyRunnable(mInstance); michael@0: michael@0: NS_DispatchToMainThread(evt); michael@0: } michael@0: } michael@0: michael@0: // static michael@0: bool michael@0: PluginDestructionGuard::DelayDestroy(nsNPAPIPluginInstance *aInstance) michael@0: { michael@0: NS_ASSERTION(NS_IsMainThread(), "Should be on the main thread"); michael@0: NS_ASSERTION(aInstance, "Uh, I need an instance!"); michael@0: michael@0: // Find the first guard on the stack and make it do a delayed michael@0: // destroy upon destruction. michael@0: michael@0: PluginDestructionGuard *g = michael@0: static_cast(PR_LIST_HEAD(&sListHead)); michael@0: michael@0: while (g != &sListHead) { michael@0: if (g->mInstance == aInstance) { michael@0: g->mDelayedDestroy = true; michael@0: michael@0: return true; michael@0: } michael@0: g = static_cast(PR_NEXT_LINK(g)); michael@0: } michael@0: michael@0: return false; michael@0: }