gecko-dev/dom/plugins/base/nsPluginTags.cpp

572 строки
16 KiB
C++

/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsPluginTags.h"
#include "prlink.h"
#include "plstr.h"
#include "nsIPluginInstanceOwner.h"
#include "nsServiceManagerUtils.h"
#include "nsPluginsDir.h"
#include "nsPluginHost.h"
#include "nsIBlocklistService.h"
#include "nsIUnicodeDecoder.h"
#include "nsIPlatformCharset.h"
#include "nsICharsetConverterManager.h"
#include "nsIDOMMimeType.h"
#include "nsPluginLogging.h"
#include "nsNPAPIPlugin.h"
#include "mozilla/TimeStamp.h"
#include "mozilla/Preferences.h"
#include <cctype>
using namespace mozilla;
using mozilla::TimeStamp;
// These legacy flags are used in the plugin registry. The states are now
// stored in prefs, but we still need to be able to import them.
#define NS_PLUGIN_FLAG_ENABLED 0x0001 // is this plugin enabled?
// no longer used 0x0002 // reuse only if regenerating pluginreg.dat
#define NS_PLUGIN_FLAG_FROMCACHE 0x0004 // this plugintag info was loaded from cache
// no longer used 0x0008 // reuse only if regenerating pluginreg.dat
#define NS_PLUGIN_FLAG_CLICKTOPLAY 0x0020 // this is a click-to-play plugin
inline char* new_str(const char* str)
{
if (str == nullptr)
return nullptr;
char* result = new char[strlen(str) + 1];
if (result != nullptr)
return strcpy(result, str);
return result;
}
static nsCString
MakePrefNameForPlugin(const char* const subname, nsPluginTag* aTag)
{
nsCString pref;
pref.AssignLiteral("plugin.");
pref.Append(subname);
pref.Append('.');
pref.Append(aTag->GetNiceFileName());
return pref;
}
static nsCString
GetStatePrefNameForPlugin(nsPluginTag* aTag)
{
return MakePrefNameForPlugin("state", aTag);
}
NS_IMPL_ISUPPORTS1(DOMMimeTypeImpl, nsIDOMMimeType)
/* nsPluginTag */
nsPluginTag::nsPluginTag(nsPluginTag* aPluginTag)
: mName(aPluginTag->mName),
mDescription(aPluginTag->mDescription),
mMimeTypes(aPluginTag->mMimeTypes),
mMimeDescriptions(aPluginTag->mMimeDescriptions),
mExtensions(aPluginTag->mExtensions),
mLibrary(nullptr),
mIsJavaPlugin(aPluginTag->mIsJavaPlugin),
mIsFlashPlugin(aPluginTag->mIsFlashPlugin),
mFileName(aPluginTag->mFileName),
mFullPath(aPluginTag->mFullPath),
mVersion(aPluginTag->mVersion),
mLastModifiedTime(0),
mNiceFileName(),
mCachedBlocklistState(nsIBlocklistService::STATE_NOT_BLOCKED),
mCachedBlocklistStateValid(false)
{
}
nsPluginTag::nsPluginTag(nsPluginInfo* aPluginInfo)
: mName(aPluginInfo->fName),
mDescription(aPluginInfo->fDescription),
mLibrary(nullptr),
mIsJavaPlugin(false),
mIsFlashPlugin(false),
mFileName(aPluginInfo->fFileName),
mFullPath(aPluginInfo->fFullPath),
mVersion(aPluginInfo->fVersion),
mLastModifiedTime(0),
mNiceFileName(),
mCachedBlocklistState(nsIBlocklistService::STATE_NOT_BLOCKED),
mCachedBlocklistStateValid(false)
{
InitMime(aPluginInfo->fMimeTypeArray,
aPluginInfo->fMimeDescriptionArray,
aPluginInfo->fExtensionArray,
aPluginInfo->fVariantCount);
EnsureMembersAreUTF8();
}
nsPluginTag::nsPluginTag(const char* aName,
const char* aDescription,
const char* aFileName,
const char* aFullPath,
const char* aVersion,
const char* const* aMimeTypes,
const char* const* aMimeDescriptions,
const char* const* aExtensions,
int32_t aVariants,
int64_t aLastModifiedTime,
bool aArgsAreUTF8)
: mName(aName),
mDescription(aDescription),
mLibrary(nullptr),
mIsJavaPlugin(false),
mIsFlashPlugin(false),
mFileName(aFileName),
mFullPath(aFullPath),
mVersion(aVersion),
mLastModifiedTime(aLastModifiedTime),
mNiceFileName(),
mCachedBlocklistState(nsIBlocklistService::STATE_NOT_BLOCKED),
mCachedBlocklistStateValid(false)
{
InitMime(aMimeTypes, aMimeDescriptions, aExtensions,
static_cast<uint32_t>(aVariants));
if (!aArgsAreUTF8)
EnsureMembersAreUTF8();
}
nsPluginTag::~nsPluginTag()
{
NS_ASSERTION(!mNext, "Risk of exhausting the stack space, bug 486349");
}
NS_IMPL_ISUPPORTS1(nsPluginTag, nsIPluginTag)
void nsPluginTag::InitMime(const char* const* aMimeTypes,
const char* const* aMimeDescriptions,
const char* const* aExtensions,
uint32_t aVariantCount)
{
if (!aMimeTypes) {
return;
}
for (uint32_t i = 0; i < aVariantCount; i++) {
if (!aMimeTypes[i] || !nsPluginHost::IsTypeWhitelisted(aMimeTypes[i])) {
continue;
}
// Look for certain special plugins.
if (nsPluginHost::IsJavaMIMEType(aMimeTypes[i])) {
mIsJavaPlugin = true;
} else if (strcmp(aMimeTypes[i], "application/x-shockwave-flash") == 0) {
mIsFlashPlugin = true;
}
// Fill in our MIME type array.
mMimeTypes.AppendElement(nsCString(aMimeTypes[i]));
// Now fill in the MIME descriptions.
if (aMimeDescriptions && aMimeDescriptions[i]) {
// we should cut off the list of suffixes which the mime
// description string may have, see bug 53895
// it is usually in form "some description (*.sf1, *.sf2)"
// so we can search for the opening round bracket
char cur = '\0';
char pre = '\0';
char * p = PL_strrchr(aMimeDescriptions[i], '(');
if (p && (p != aMimeDescriptions[i])) {
if ((p - 1) && *(p - 1) == ' ') {
pre = *(p - 1);
*(p - 1) = '\0';
} else {
cur = *p;
*p = '\0';
}
}
mMimeDescriptions.AppendElement(nsCString(aMimeDescriptions[i]));
// restore the original string
if (cur != '\0') {
*p = cur;
}
if (pre != '\0') {
*(p - 1) = pre;
}
} else {
mMimeDescriptions.AppendElement(nsCString());
}
// Now fill in the extensions.
if (aExtensions && aExtensions[i]) {
mExtensions.AppendElement(nsCString(aExtensions[i]));
} else {
mExtensions.AppendElement(nsCString());
}
}
}
#if !defined(XP_WIN) && !defined(XP_MACOSX)
static nsresult ConvertToUTF8(nsIUnicodeDecoder *aUnicodeDecoder,
nsAFlatCString& aString)
{
int32_t numberOfBytes = aString.Length();
int32_t outUnicodeLen;
nsAutoString buffer;
nsresult rv = aUnicodeDecoder->GetMaxLength(aString.get(), numberOfBytes,
&outUnicodeLen);
NS_ENSURE_SUCCESS(rv, rv);
if (!buffer.SetLength(outUnicodeLen, fallible_t()))
return NS_ERROR_OUT_OF_MEMORY;
rv = aUnicodeDecoder->Convert(aString.get(), &numberOfBytes,
buffer.BeginWriting(), &outUnicodeLen);
NS_ENSURE_SUCCESS(rv, rv);
buffer.SetLength(outUnicodeLen);
CopyUTF16toUTF8(buffer, aString);
return NS_OK;
}
#endif
nsresult nsPluginTag::EnsureMembersAreUTF8()
{
#if defined(XP_WIN) || defined(XP_MACOSX)
return NS_OK;
#else
nsresult rv;
nsCOMPtr<nsIPlatformCharset> pcs =
do_GetService(NS_PLATFORMCHARSET_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIUnicodeDecoder> decoder;
nsCOMPtr<nsICharsetConverterManager> ccm =
do_GetService(NS_CHARSETCONVERTERMANAGER_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
nsAutoCString charset;
rv = pcs->GetCharset(kPlatformCharsetSel_FileName, charset);
NS_ENSURE_SUCCESS(rv, rv);
if (!charset.LowerCaseEqualsLiteral("utf-8")) {
rv = ccm->GetUnicodeDecoderRaw(charset.get(), getter_AddRefs(decoder));
NS_ENSURE_SUCCESS(rv, rv);
ConvertToUTF8(decoder, mFileName);
ConvertToUTF8(decoder, mFullPath);
}
// The description of the plug-in and the various MIME type descriptions
// should be encoded in the standard plain text file encoding for this system.
// XXX should we add kPlatformCharsetSel_PluginResource?
rv = pcs->GetCharset(kPlatformCharsetSel_PlainTextInFile, charset);
NS_ENSURE_SUCCESS(rv, rv);
if (!charset.LowerCaseEqualsLiteral("utf-8")) {
rv = ccm->GetUnicodeDecoderRaw(charset.get(), getter_AddRefs(decoder));
NS_ENSURE_SUCCESS(rv, rv);
ConvertToUTF8(decoder, mName);
ConvertToUTF8(decoder, mDescription);
for (uint32_t i = 0; i < mMimeDescriptions.Length(); ++i) {
ConvertToUTF8(decoder, mMimeDescriptions[i]);
}
}
return NS_OK;
#endif
}
NS_IMETHODIMP
nsPluginTag::GetDescription(nsACString& aDescription)
{
aDescription = mDescription;
return NS_OK;
}
NS_IMETHODIMP
nsPluginTag::GetFilename(nsACString& aFileName)
{
aFileName = mFileName;
return NS_OK;
}
NS_IMETHODIMP
nsPluginTag::GetFullpath(nsACString& aFullPath)
{
aFullPath = mFullPath;
return NS_OK;
}
NS_IMETHODIMP
nsPluginTag::GetVersion(nsACString& aVersion)
{
aVersion = mVersion;
return NS_OK;
}
NS_IMETHODIMP
nsPluginTag::GetName(nsACString& aName)
{
aName = mName;
return NS_OK;
}
bool
nsPluginTag::IsActive()
{
return IsEnabled() && !IsBlocklisted();
}
bool
nsPluginTag::IsEnabled()
{
const PluginState state = GetPluginState();
return (state == ePluginState_Enabled) || (state == ePluginState_Clicktoplay);
}
NS_IMETHODIMP
nsPluginTag::GetDisabled(bool* aDisabled)
{
*aDisabled = !IsEnabled();
return NS_OK;
}
bool
nsPluginTag::IsBlocklisted()
{
return GetBlocklistState() == nsIBlocklistService::STATE_BLOCKED;
}
NS_IMETHODIMP
nsPluginTag::GetBlocklisted(bool* aBlocklisted)
{
*aBlocklisted = IsBlocklisted();
return NS_OK;
}
bool
nsPluginTag::IsClicktoplay()
{
const PluginState state = GetPluginState();
return (state == ePluginState_Clicktoplay);
}
NS_IMETHODIMP
nsPluginTag::GetClicktoplay(bool *aClicktoplay)
{
*aClicktoplay = IsClicktoplay();
return NS_OK;
}
NS_IMETHODIMP
nsPluginTag::GetEnabledState(uint32_t *aEnabledState) {
int32_t enabledState;
nsresult rv = Preferences::GetInt(GetStatePrefNameForPlugin(this).get(),
&enabledState);
if (NS_SUCCEEDED(rv) &&
enabledState >= nsIPluginTag::STATE_DISABLED &&
enabledState <= nsIPluginTag::STATE_ENABLED) {
*aEnabledState = (uint32_t)enabledState;
return rv;
}
enabledState = Preferences::GetInt("plugin.default.state",
nsIPluginTag::STATE_ENABLED);
if (enabledState >= nsIPluginTag::STATE_DISABLED &&
enabledState <= nsIPluginTag::STATE_ENABLED) {
*aEnabledState = (uint32_t)enabledState;
return NS_OK;
}
return NS_ERROR_UNEXPECTED;
}
NS_IMETHODIMP
nsPluginTag::SetEnabledState(uint32_t aEnabledState) {
if (aEnabledState >= ePluginState_MaxValue)
return NS_ERROR_ILLEGAL_VALUE;
uint32_t oldState = nsIPluginTag::STATE_DISABLED;
GetEnabledState(&oldState);
if (oldState != aEnabledState) {
Preferences::SetInt(GetStatePrefNameForPlugin(this).get(), aEnabledState);
if (nsRefPtr<nsPluginHost> host = nsPluginHost::GetInst()) {
host->UpdatePluginInfo(this);
}
}
return NS_OK;
}
nsPluginTag::PluginState
nsPluginTag::GetPluginState()
{
uint32_t enabledState = nsIPluginTag::STATE_DISABLED;
GetEnabledState(&enabledState);
return (PluginState)enabledState;
}
void
nsPluginTag::SetPluginState(PluginState state)
{
MOZ_STATIC_ASSERT((uint32_t)nsPluginTag::ePluginState_Disabled == nsIPluginTag::STATE_DISABLED, "nsPluginTag::ePluginState_Disabled must match nsIPluginTag::STATE_DISABLED");
MOZ_STATIC_ASSERT((uint32_t)nsPluginTag::ePluginState_Clicktoplay == nsIPluginTag::STATE_CLICKTOPLAY, "nsPluginTag::ePluginState_Clicktoplay must match nsIPluginTag::STATE_CLICKTOPLAY");
MOZ_STATIC_ASSERT((uint32_t)nsPluginTag::ePluginState_Enabled == nsIPluginTag::STATE_ENABLED, "nsPluginTag::ePluginState_Enabled must match nsIPluginTag::STATE_ENABLED");
SetEnabledState((uint32_t)state);
}
NS_IMETHODIMP
nsPluginTag::GetMimeTypes(uint32_t* aCount, PRUnichar*** aResults)
{
uint32_t count = mMimeTypes.Length();
*aResults = static_cast<PRUnichar**>
(nsMemory::Alloc(count * sizeof(**aResults)));
if (!*aResults)
return NS_ERROR_OUT_OF_MEMORY;
*aCount = count;
for (uint32_t i = 0; i < count; i++) {
(*aResults)[i] = ToNewUnicode(mMimeTypes[i]);
}
return NS_OK;
}
NS_IMETHODIMP
nsPluginTag::GetMimeDescriptions(uint32_t* aCount, PRUnichar*** aResults)
{
uint32_t count = mMimeDescriptions.Length();
*aResults = static_cast<PRUnichar**>
(nsMemory::Alloc(count * sizeof(**aResults)));
if (!*aResults)
return NS_ERROR_OUT_OF_MEMORY;
*aCount = count;
for (uint32_t i = 0; i < count; i++) {
(*aResults)[i] = ToNewUnicode(mMimeDescriptions[i]);
}
return NS_OK;
}
NS_IMETHODIMP
nsPluginTag::GetExtensions(uint32_t* aCount, PRUnichar*** aResults)
{
uint32_t count = mExtensions.Length();
*aResults = static_cast<PRUnichar**>
(nsMemory::Alloc(count * sizeof(**aResults)));
if (!*aResults)
return NS_ERROR_OUT_OF_MEMORY;
*aCount = count;
for (uint32_t i = 0; i < count; i++) {
(*aResults)[i] = ToNewUnicode(mExtensions[i]);
}
return NS_OK;
}
bool
nsPluginTag::HasSameNameAndMimes(const nsPluginTag *aPluginTag) const
{
NS_ENSURE_TRUE(aPluginTag, false);
if ((!mName.Equals(aPluginTag->mName)) ||
(mMimeTypes.Length() != aPluginTag->mMimeTypes.Length())) {
return false;
}
for (uint32_t i = 0; i < mMimeTypes.Length(); i++) {
if (!mMimeTypes[i].Equals(aPluginTag->mMimeTypes[i])) {
return false;
}
}
return true;
}
void nsPluginTag::TryUnloadPlugin(bool inShutdown)
{
// We never want to send NPP_Shutdown to an in-process plugin unless
// this process is shutting down.
if (mLibrary && !inShutdown) {
return;
}
if (mPlugin) {
mPlugin->Shutdown();
mPlugin = nullptr;
}
}
nsCString nsPluginTag::GetNiceFileName() {
if (!mNiceFileName.IsEmpty()) {
return mNiceFileName;
}
if (mIsFlashPlugin) {
mNiceFileName.Assign(NS_LITERAL_CSTRING("flash"));
return mNiceFileName;
}
if (mIsJavaPlugin) {
mNiceFileName.Assign(NS_LITERAL_CSTRING("java"));
return mNiceFileName;
}
mNiceFileName.Assign(mFileName);
int32_t niceNameLength = mFileName.RFind(".");
NS_ASSERTION(niceNameLength != kNotFound, "mFileName doesn't have a '.'?");
while (niceNameLength > 0) {
char chr = mFileName[niceNameLength - 1];
if (!std::isalpha(chr))
niceNameLength--;
else
break;
}
// If it turns out that niceNameLength <= 0, we'll fall back and use the
// entire mFileName (which we've already taken care of, a few lines back)
if (niceNameLength > 0) {
mNiceFileName.Truncate(niceNameLength);
}
ToLowerCase(mNiceFileName);
return mNiceFileName;
}
void nsPluginTag::ImportFlagsToPrefs(uint32_t flags)
{
if (!(flags & NS_PLUGIN_FLAG_ENABLED)) {
SetPluginState(ePluginState_Disabled);
}
}
uint32_t
nsPluginTag::GetBlocklistState()
{
if (mCachedBlocklistStateValid) {
return mCachedBlocklistState;
}
nsCOMPtr<nsIBlocklistService> blocklist = do_GetService("@mozilla.org/extensions/blocklist;1");
if (!blocklist) {
return nsIBlocklistService::STATE_NOT_BLOCKED;
}
// The EmptyString()s are so we use the currently running application
// and toolkit versions
uint32_t state;
if (NS_FAILED(blocklist->GetPluginBlocklistState(this, EmptyString(),
EmptyString(), &state))) {
return nsIBlocklistService::STATE_NOT_BLOCKED;
}
MOZ_ASSERT(state <= UINT16_MAX);
mCachedBlocklistState = (uint16_t) state;
mCachedBlocklistStateValid = true;
return state;
}
void
nsPluginTag::InvalidateBlocklistState()
{
mCachedBlocklistStateValid = false;
}