/* -*- 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 "prenv.h" #include "nsPluginHost.h" #include "nsIBlocklistService.h" #include "nsPluginLogging.h" #include "nsCharSeparatedTokenizer.h" #include "mozilla/Preferences.h" #include "mozilla/Unused.h" #include "nsNetUtil.h" #include #include "mozilla/Encoding.h" #include "mozilla/dom/FakePluginTagInitBinding.h" using mozilla::dom::FakePluginTagInit; using namespace mozilla; // check comma delimited extensions static bool ExtensionInList(const nsCString& aExtensionList, const nsACString& aExtension) { for (const nsACString& extension : nsCCharSeparatedTokenizer(aExtensionList, ',').ToRange()) { if (extension.Equals(aExtension, nsCaseInsensitiveCStringComparator)) { return true; } } return false; } // Search for an extension in an extensions array, and return its // matching mime type static bool SearchExtensions(const nsTArray& aExtensions, const nsTArray& aMimeTypes, const nsACString& aFindExtension, nsACString& aMatchingType) { uint32_t mimes = aMimeTypes.Length(); MOZ_ASSERT(mimes == aExtensions.Length(), "These arrays should have matching elements"); aMatchingType.Truncate(); for (uint32_t i = 0; i < mimes; i++) { if (ExtensionInList(aExtensions[i], aFindExtension)) { aMatchingType = aMimeTypes[i]; return true; } } return false; } static nsCString MakeNiceFileName(const nsCString& aFileName) { nsCString niceName = aFileName; int32_t niceNameLength = aFileName.RFind("."); NS_ASSERTION(niceNameLength != kNotFound, "aFileName doesn't have a '.'?"); while (niceNameLength > 0) { char chr = aFileName[niceNameLength - 1]; if (!std::isalpha(chr)) niceNameLength--; else break; } // If it turns out that niceNameLength <= 0, we'll fall back and use the // entire aFileName (which we've already taken care of, a few lines back). if (niceNameLength > 0) { niceName.Truncate(niceNameLength); } ToLowerCase(niceName); return niceName; } static nsCString MakePrefNameForPlugin(const char* const subname, nsIInternalPluginTag* aTag) { nsCString pref; nsAutoCString pluginName(aTag->GetNiceFileName()); if (pluginName.IsEmpty()) { // Use filename if nice name fails pluginName = aTag->FileName(); if (pluginName.IsEmpty()) { MOZ_ASSERT_UNREACHABLE("Plugin with no filename or nice name in list"); pluginName.AssignLiteral("unknown-plugin-name"); } } pref.AssignLiteral("plugin."); pref.Append(subname); pref.Append('.'); pref.Append(pluginName); return pref; } static nsCString GetStatePrefNameForPlugin(nsIInternalPluginTag* aTag) { return MakePrefNameForPlugin("state", aTag); } static nsresult IsEnabledStateLockedForPlugin(nsIInternalPluginTag* aTag, bool* aIsEnabledStateLocked) { *aIsEnabledStateLocked = false; nsCOMPtr prefs(do_GetService(NS_PREFSERVICE_CONTRACTID)); if (NS_WARN_IF(!prefs)) { return NS_ERROR_FAILURE; } Unused << prefs->PrefIsLocked(GetStatePrefNameForPlugin(aTag).get(), aIsEnabledStateLocked); return NS_OK; } /* nsIInternalPluginTag */ uint32_t nsIInternalPluginTag::sNextId; nsIInternalPluginTag::nsIInternalPluginTag() = default; nsIInternalPluginTag::nsIInternalPluginTag(const char* aName, const char* aDescription, const char* aFileName, const char* aVersion) : mName(aName), mDescription(aDescription), mFileName(aFileName), mVersion(aVersion) {} nsIInternalPluginTag::nsIInternalPluginTag( const char* aName, const char* aDescription, const char* aFileName, const char* aVersion, const nsTArray& aMimeTypes, const nsTArray& aMimeDescriptions, const nsTArray& aExtensions) : mName(aName), mDescription(aDescription), mFileName(aFileName), mVersion(aVersion), mMimeTypes(aMimeTypes.Clone()), mMimeDescriptions(aMimeDescriptions.Clone()), mExtensions(aExtensions.Clone()) {} nsIInternalPluginTag::~nsIInternalPluginTag() = default; bool nsIInternalPluginTag::HasExtension(const nsACString& aExtension, nsACString& aMatchingType) const { return SearchExtensions(mExtensions, mMimeTypes, aExtension, aMatchingType); } bool nsIInternalPluginTag::HasMimeType(const nsACString& aMimeType) const { return mMimeTypes.Contains(aMimeType, nsCaseInsensitiveCStringArrayComparator()); } /* nsFakePluginTag */ nsFakePluginTag::nsFakePluginTag() : mId(sNextId++), mState(ePluginState_Disabled) {} nsFakePluginTag::nsFakePluginTag(uint32_t aId, already_AddRefed&& aHandlerURI, const char* aName, const char* aDescription, const nsTArray& aMimeTypes, const nsTArray& aMimeDescriptions, const nsTArray& aExtensions, const nsCString& aNiceName, const nsString& aSandboxScript) : nsIInternalPluginTag(aName, aDescription, nullptr, nullptr, aMimeTypes, aMimeDescriptions, aExtensions), mId(aId), mHandlerURI(aHandlerURI), mNiceName(aNiceName), mSandboxScript(aSandboxScript), mState(ePluginState_Enabled) {} nsFakePluginTag::~nsFakePluginTag() = default; NS_IMPL_ADDREF(nsFakePluginTag) NS_IMPL_RELEASE(nsFakePluginTag) NS_INTERFACE_TABLE_HEAD(nsFakePluginTag) NS_INTERFACE_TABLE_BEGIN NS_INTERFACE_TABLE_ENTRY_AMBIGUOUS(nsFakePluginTag, nsIPluginTag, nsIInternalPluginTag) NS_INTERFACE_TABLE_ENTRY(nsFakePluginTag, nsIInternalPluginTag) NS_INTERFACE_TABLE_ENTRY_AMBIGUOUS(nsFakePluginTag, nsISupports, nsIInternalPluginTag) NS_INTERFACE_TABLE_ENTRY(nsFakePluginTag, nsIFakePluginTag) NS_INTERFACE_TABLE_END NS_INTERFACE_TABLE_TAIL /* static */ nsresult nsFakePluginTag::Create(const FakePluginTagInit& aInitDictionary, nsFakePluginTag** aPluginTag) { NS_ENSURE_TRUE(sNextId <= PR_INT32_MAX, NS_ERROR_OUT_OF_MEMORY); NS_ENSURE_TRUE(!aInitDictionary.mMimeEntries.IsEmpty(), NS_ERROR_INVALID_ARG); RefPtr tag = new nsFakePluginTag(); nsresult rv = NS_NewURI(getter_AddRefs(tag->mHandlerURI), aInitDictionary.mHandlerURI); NS_ENSURE_SUCCESS(rv, rv); CopyUTF16toUTF8(aInitDictionary.mNiceName, tag->mNiceName); CopyUTF16toUTF8(aInitDictionary.mFullPath, tag->mFullPath); CopyUTF16toUTF8(aInitDictionary.mName, tag->mName); CopyUTF16toUTF8(aInitDictionary.mDescription, tag->mDescription); CopyUTF16toUTF8(aInitDictionary.mFileName, tag->mFileName); CopyUTF16toUTF8(aInitDictionary.mVersion, tag->mVersion); tag->mSandboxScript = aInitDictionary.mSandboxScript; for (const mozilla::dom::FakePluginMimeEntry& mimeEntry : aInitDictionary.mMimeEntries) { CopyUTF16toUTF8(mimeEntry.mType, *tag->mMimeTypes.AppendElement()); CopyUTF16toUTF8(mimeEntry.mDescription, *tag->mMimeDescriptions.AppendElement()); CopyUTF16toUTF8(mimeEntry.mExtension, *tag->mExtensions.AppendElement()); } tag.forget(aPluginTag); return NS_OK; } bool nsFakePluginTag::HandlerURIMatches(nsIURI* aURI) { bool equals = false; return NS_SUCCEEDED(mHandlerURI->Equals(aURI, &equals)) && equals; } NS_IMETHODIMP nsFakePluginTag::GetHandlerURI(nsIURI** aResult) { NS_IF_ADDREF(*aResult = mHandlerURI); return NS_OK; } NS_IMETHODIMP nsFakePluginTag::GetSandboxScript(nsAString& aSandboxScript) { aSandboxScript = mSandboxScript; return NS_OK; } NS_IMETHODIMP nsFakePluginTag::GetDescription(/* utf-8 */ nsACString& aResult) { aResult = mDescription; return NS_OK; } NS_IMETHODIMP nsFakePluginTag::GetIsFlashPlugin(bool* aIsFlash) { *aIsFlash = false; return NS_OK; } NS_IMETHODIMP nsFakePluginTag::GetFilename(/* utf-8 */ nsACString& aResult) { aResult = mFileName; return NS_OK; } NS_IMETHODIMP nsFakePluginTag::GetFullpath(/* utf-8 */ nsACString& aResult) { aResult = mFullPath; return NS_OK; } NS_IMETHODIMP nsFakePluginTag::GetVersion(/* utf-8 */ nsACString& aResult) { aResult = mVersion; return NS_OK; } NS_IMETHODIMP nsFakePluginTag::GetName(/* utf-8 */ nsACString& aResult) { aResult = mName; return NS_OK; } const nsCString& nsFakePluginTag::GetNiceFileName() { // We don't try to mimic the special-cased flash/java names if the fake plugin // claims one of their MIME types, but do allow directly setting niceName if // emulating those is desired. if (mNiceName.IsEmpty() && !mFileName.IsEmpty()) { mNiceName = MakeNiceFileName(mFileName); } return mNiceName; } NS_IMETHODIMP nsFakePluginTag::GetNiceName(/* utf-8 */ nsACString& aResult) { aResult = GetNiceFileName(); return NS_OK; } NS_IMETHODIMP nsFakePluginTag::GetBlocklistState(uint32_t* aResult) { // Fake tags don't currently support blocklisting *aResult = nsIBlocklistService::STATE_NOT_BLOCKED; return NS_OK; } NS_IMETHODIMP nsFakePluginTag::GetBlocklisted(bool* aBlocklisted) { // Fake tags can't be blocklisted *aBlocklisted = false; return NS_OK; } NS_IMETHODIMP nsFakePluginTag::GetIsEnabledStateLocked(bool* aIsEnabledStateLocked) { return IsEnabledStateLockedForPlugin(this, aIsEnabledStateLocked); } bool nsFakePluginTag::IsEnabled() { return mState == ePluginState_Enabled || mState == ePluginState_Clicktoplay; } NS_IMETHODIMP nsFakePluginTag::GetDisabled(bool* aDisabled) { *aDisabled = !IsEnabled(); return NS_OK; } NS_IMETHODIMP nsFakePluginTag::GetClicktoplay(bool* aClicktoplay) { *aClicktoplay = (mState == ePluginState_Clicktoplay); return NS_OK; } NS_IMETHODIMP nsFakePluginTag::GetEnabledState(uint32_t* aEnabledState) { *aEnabledState = (uint32_t)mState; return NS_OK; } NS_IMETHODIMP nsFakePluginTag::SetEnabledState(uint32_t aEnabledState) { // There are static asserts above enforcing that this enum matches mState = (PluginState)aEnabledState; // FIXME-jsplugins update return NS_OK; } NS_IMETHODIMP nsFakePluginTag::GetMimeTypes(nsTArray& aResults) { aResults = mMimeTypes.Clone(); return NS_OK; } NS_IMETHODIMP nsFakePluginTag::GetMimeDescriptions(nsTArray& aResults) { aResults = mMimeDescriptions.Clone(); return NS_OK; } NS_IMETHODIMP nsFakePluginTag::GetExtensions(nsTArray& aResults) { aResults = mExtensions.Clone(); return NS_OK; } NS_IMETHODIMP nsFakePluginTag::GetActive(bool* aResult) { // Fake plugins can't be blocklisted, so this is just !Disabled *aResult = IsEnabled(); return NS_OK; } NS_IMETHODIMP nsFakePluginTag::GetLastModifiedTime(PRTime* aLastModifiedTime) { // FIXME-jsplugins What should this return, if anything? MOZ_ASSERT(aLastModifiedTime); *aLastModifiedTime = 0; return NS_OK; } // We don't load fake plugins out of a library, so they should always be there. NS_IMETHODIMP nsFakePluginTag::GetLoaded(bool* ret) { *ret = true; return NS_OK; } NS_IMETHODIMP nsFakePluginTag::GetId(uint32_t* aId) { *aId = mId; return NS_OK; }