diff options
Diffstat (limited to 'dom/plugins')
22 files changed, 1354 insertions, 0 deletions
diff --git a/dom/plugins/base/moz.build b/dom/plugins/base/moz.build new file mode 100644 index 0000000000..1e13483336 --- /dev/null +++ b/dom/plugins/base/moz.build @@ -0,0 +1,45 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# 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/. + +XPIDL_SOURCES += [ + "nsIPluginTag.idl", +] + +XPIDL_MODULE = "plugin" + +EXPORTS += [ + "nsPluginHost.h", + "nsPluginLogging.h", + "nsPluginTags.h", +] + +UNIFIED_SOURCES += [ + "nsPluginHost.cpp", + "nsPluginTags.cpp", +] + +LOCAL_INCLUDES += [ + "/dom/base", + "/gfx/cairo/cairo/src", + "/layout/generic", + "/layout/xul", + "/netwerk/base", + "/widget", + "/widget/cocoa", + "/xpcom/base", +] + +if CONFIG["OS_ARCH"] == "WINNT": + LOCAL_INCLUDES += [ + "/xpcom/base", + ] + +include("/ipc/chromium/chromium-config.mozbuild") + +FINAL_LIBRARY = "xul" + +if CONFIG["MOZ_WIDGET_TOOLKIT"] == "gtk": + CXXFLAGS += CONFIG["MOZ_GTK3_CFLAGS"] diff --git a/dom/plugins/base/nsIPluginTag.idl b/dom/plugins/base/nsIPluginTag.idl new file mode 100644 index 0000000000..68f8da1dc8 --- /dev/null +++ b/dom/plugins/base/nsIPluginTag.idl @@ -0,0 +1,97 @@ +/* -*- Mode: IDL; 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 "nsISupports.idl" + +interface nsIURI; + +[builtinclass, scriptable, uuid(5daa99d5-265a-4397-b429-c943803e2619)] +interface nsIPluginTag : nsISupports +{ + // enabledState is stored as one of the following as an integer in prefs, + // so if new states are added, they must not renumber the existing states. + const unsigned long STATE_DISABLED = 0; + const unsigned long STATE_CLICKTOPLAY = 1; + const unsigned long STATE_ENABLED = 2; + + readonly attribute AUTF8String description; + readonly attribute AUTF8String filename; + readonly attribute AUTF8String fullpath; + readonly attribute AUTF8String version; + readonly attribute AUTF8String name; + + // The 'nice' name of this plugin, e.g. 'flash' 'java' + readonly attribute AUTF8String niceName; + + /** + * true only if this plugin is "hardblocked" and cannot be enabled. + */ + // FIXME-jsplugins QI to fakePluginTag possible + // FIXME-jsplugins implement missing + tests (whatever that means) + [infallible] + readonly attribute boolean blocklisted; + + /** + * true if the state is non-default and locked, false otherwise. + */ + [infallible] + readonly attribute boolean isEnabledStateLocked; + + // If this plugin is capable of being used (not disabled, blocklisted, etc) + [infallible] + readonly attribute boolean active; + + // Get a specific nsIBlocklistService::STATE_* + [infallible] + readonly attribute unsigned long blocklistState; + + [infallible] + readonly attribute boolean disabled; + [infallible] + readonly attribute boolean clicktoplay; + [infallible] + readonly attribute boolean loaded; + // See the STATE_* values above. + attribute unsigned long enabledState; + + readonly attribute PRTime lastModifiedTime; + + readonly attribute boolean isFlashPlugin; + + Array<AUTF8String> getMimeTypes(); + Array<AUTF8String> getMimeDescriptions(); + Array<AUTF8String> getExtensions(); + + /** + * An id for this plugin. 0 is a valid id. + */ + readonly attribute unsigned long id; +}; + +/** + * An interface representing a "fake" plugin: one implemented in JavaScript, not + * as a NPAPI plug-in. See nsIPluginHost.registerFakePlugin and the + * documentation for the FakePluginTagInit dictionary. + */ +[builtinclass, scriptable, uuid(6d22c968-226d-4156-b230-da6ad6bbf6e8)] +interface nsIFakePluginTag : nsIPluginTag +{ + /** + * The URI that should be loaded into the tag (as a frame) to handle the + * plugin. Note that the original data/src value for the plugin is not loaded + * and will need to be requested by the handler via XHR or similar if desired. + */ + readonly attribute nsIURI handlerURI; + + /** + * Optional script to run in a sandbox when instantiating a plugin. If this + * value is an empty string then no such script will be run. + * The script runs in a sandbox with system principal in the process that + * contains the element that instantiates the plugin (ie the EMBED or OBJECT + * element). The sandbox global has a 'pluginElement' property that the script + * can use to access the element that instantiates the plugin. + */ + readonly attribute AString sandboxScript; +}; diff --git a/dom/plugins/base/nsPluginHost.cpp b/dom/plugins/base/nsPluginHost.cpp new file mode 100644 index 0000000000..1c94e60002 --- /dev/null +++ b/dom/plugins/base/nsPluginHost.cpp @@ -0,0 +1,223 @@ +/* -*- 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/. */ + +/* nsPluginHost.cpp - top-level plugin management code */ + +#include "nsPluginHost.h" + +#include "nscore.h" + +#include <cstdlib> +#include <stdio.h> +#include "nsPluginLogging.h" +#include "mozilla/Preferences.h" +#include "mozilla/ProfilerLabels.h" +#include "nsIBlocklistService.h" +#include "mozilla/ClearOnShutdown.h" +#include "nsXULAppAPI.h" + +using namespace mozilla; + +LazyLogModule nsPluginLogging::gNPNLog(NPN_LOG_NAME); +LazyLogModule nsPluginLogging::gNPPLog(NPP_LOG_NAME); +LazyLogModule nsPluginLogging::gPluginLog(PLUGIN_LOG_NAME); + +StaticRefPtr<nsPluginHost> nsPluginHost::sInst; + +nsPluginHost::nsPluginHost() : mPluginEpoch(0) { +#ifdef PLUGIN_LOGGING + MOZ_LOG(nsPluginLogging::gNPNLog, PLUGIN_LOG_ALWAYS, + ("NPN Logging Active!\n")); + MOZ_LOG(nsPluginLogging::gPluginLog, PLUGIN_LOG_ALWAYS, + ("General Plugin Logging Active! (nsPluginHost::ctor)\n")); + MOZ_LOG(nsPluginLogging::gNPPLog, PLUGIN_LOG_ALWAYS, + ("NPP Logging Active!\n")); + + PLUGIN_LOG(PLUGIN_LOG_ALWAYS, ("nsPluginHost::ctor\n")); + PR_LogFlush(); +#endif + + // Load plugins on creation, as there's a good chance we'll need to send them + // to content processes directly after creation. + if (XRE_IsParentProcess()) { + // Always increment the chrome epoch when we bring up the nsPluginHost in + // the parent process. + IncrementChromeEpoch(); + } +} + +nsPluginHost::~nsPluginHost() { + PLUGIN_LOG(PLUGIN_LOG_ALWAYS, ("nsPluginHost::dtor\n")); +} + +NS_IMPL_ISUPPORTS(nsPluginHost, nsISupportsWeakReference) + +already_AddRefed<nsPluginHost> nsPluginHost::GetInst() { + if (!sInst) { + sInst = new nsPluginHost(); + ClearOnShutdown(&sInst); + } + + return do_AddRef(sInst); +} + +bool nsPluginHost::HavePluginForType(const nsACString& aMimeType, + PluginFilter aFilter) { + bool checkEnabled = aFilter & eExcludeDisabled; + bool allowFake = !(aFilter & eExcludeFake); + return FindPluginForType(aMimeType, allowFake, checkEnabled); +} + +nsIInternalPluginTag* nsPluginHost::FindPluginForType( + const nsACString& aMimeType, bool aIncludeFake, bool aCheckEnabled) { + if (aIncludeFake) { + return FindFakePluginForType(aMimeType, aCheckEnabled); + } + + return nullptr; +} + +NS_IMETHODIMP +nsPluginHost::GetPluginTagForType(const nsACString& aMimeType, + uint32_t aExcludeFlags, + nsIPluginTag** aResult) { + bool includeFake = !(aExcludeFlags & eExcludeFake); + bool includeDisabled = !(aExcludeFlags & eExcludeDisabled); + + // First look for an enabled plugin. + RefPtr<nsIInternalPluginTag> tag = + FindPluginForType(aMimeType, includeFake, true); + if (!tag && includeDisabled) { + tag = FindPluginForType(aMimeType, includeFake, false); + } + + if (tag) { + tag.forget(aResult); + return NS_OK; + } + + return NS_ERROR_NOT_AVAILABLE; +} + +NS_IMETHODIMP +nsPluginHost::GetPermissionStringForTag(nsIPluginTag* aTag, + uint32_t aExcludeFlags, + nsACString& aPermissionString) { + NS_ENSURE_TRUE(aTag, NS_ERROR_FAILURE); + + aPermissionString.Truncate(); + uint32_t blocklistState; + nsresult rv = aTag->GetBlocklistState(&blocklistState); + NS_ENSURE_SUCCESS(rv, rv); + + aPermissionString.AssignLiteral("plugin:"); + + nsCString niceName; + rv = aTag->GetNiceName(niceName); + NS_ENSURE_SUCCESS(rv, rv); + NS_ENSURE_TRUE(!niceName.IsEmpty(), NS_ERROR_FAILURE); + + aPermissionString.Append(niceName); + + return NS_OK; +} + +nsFakePluginTag* nsPluginHost::FindFakePluginForType( + const nsACString& aMimeType, bool aCheckEnabled) { + int32_t numFakePlugins = mFakePlugins.Length(); + for (int32_t i = 0; i < numFakePlugins; i++) { + nsFakePluginTag* plugin = mFakePlugins[i]; + bool active; + if ((!aCheckEnabled || + (NS_SUCCEEDED(plugin->GetActive(&active)) && active)) && + plugin->HasMimeType(aMimeType)) { + return plugin; + } + } + + return nullptr; +} + +static bool MimeTypeIsAllowedForFakePlugin(const nsString& aMimeType) { + static const char* const allowedFakePlugins[] = { + // PDF + "application/pdf", + "application/vnd.adobe.pdf", + "application/vnd.adobe.pdfxml", + "application/vnd.adobe.x-mars", + "application/vnd.adobe.xdp+xml", + "application/vnd.adobe.xfdf", + "application/vnd.adobe.xfd+xml", + "application/vnd.fdf", + }; + + for (const auto allowed : allowedFakePlugins) { + if (aMimeType.EqualsASCII(allowed)) { + return true; + } + } + return false; +} + +nsPluginHost::SpecialType nsPluginHost::GetSpecialType( + const nsACString& aMIMEType) { + if (aMIMEType.LowerCaseEqualsASCII("application/x-test")) { + return eSpecialType_Test; + } + + if (aMIMEType.LowerCaseEqualsASCII("application/x-shockwave-flash") || + aMIMEType.LowerCaseEqualsASCII("application/futuresplash") || + aMIMEType.LowerCaseEqualsASCII("application/x-shockwave-flash-test")) { + return eSpecialType_Flash; + } + + return eSpecialType_None; +} + +// Check whether or not a tag is a live, valid tag, and that it's loaded. +bool nsPluginHost::IsLiveTag(nsIPluginTag* aPluginTag) { + nsCOMPtr<nsIInternalPluginTag> internalTag(do_QueryInterface(aPluginTag)); + uint32_t fakeCount = mFakePlugins.Length(); + for (uint32_t i = 0; i < fakeCount; i++) { + if (mFakePlugins[i] == internalTag) { + return true; + } + } + return false; +} + +void nsPluginHost::IncrementChromeEpoch() { + MOZ_ASSERT(XRE_IsParentProcess()); + mPluginEpoch++; +} + +uint32_t nsPluginHost::ChromeEpoch() { + MOZ_ASSERT(XRE_IsParentProcess()); + return mPluginEpoch; +} + +uint32_t nsPluginHost::ChromeEpochForContent() { + MOZ_ASSERT(XRE_IsContentProcess()); + return mPluginEpoch; +} + +void nsPluginHost::SetChromeEpochForContent(uint32_t aEpoch) { + MOZ_ASSERT(XRE_IsContentProcess()); + mPluginEpoch = aEpoch; +} + +/* static */ +bool nsPluginHost::CanUsePluginForMIMEType(const nsACString& aMIMEType) { + // We "support" these in the sense that we show a special transparent + // fallback element in their place. + if (nsPluginHost::GetSpecialType(aMIMEType) == + nsPluginHost::eSpecialType_Flash || + MimeTypeIsAllowedForFakePlugin(NS_ConvertUTF8toUTF16(aMIMEType)) || + aMIMEType.LowerCaseEqualsLiteral("application/x-test")) { + return true; + } + + return false; +} diff --git a/dom/plugins/base/nsPluginHost.h b/dom/plugins/base/nsPluginHost.h new file mode 100644 index 0000000000..6566e979f3 --- /dev/null +++ b/dom/plugins/base/nsPluginHost.h @@ -0,0 +1,111 @@ +/* -*- 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/. */ + +#ifndef nsPluginHost_h_ +#define nsPluginHost_h_ + +#include "mozilla/StaticPtr.h" + +#include "nsCOMPtr.h" +#include "prlink.h" +#include "nsIPluginTag.h" +#include "nsWeakReference.h" +#include "nsTArray.h" +#include "nsPluginTags.h" + +class nsIFile; + +class nsPluginHost final : public nsSupportsWeakReference { + friend class nsFakePluginTag; + virtual ~nsPluginHost(); + + public: + nsPluginHost(); + + static already_AddRefed<nsPluginHost> GetInst(); + + NS_DECL_ISUPPORTS + + // Acts like a bitfield + enum PluginFilter { eExcludeNone, eExcludeDisabled, eExcludeFake }; + + NS_IMETHOD GetPluginTagForType(const nsACString& aMimeType, + uint32_t aExcludeFlags, + nsIPluginTag** aResult); + NS_IMETHOD GetPermissionStringForTag(nsIPluginTag* aTag, + uint32_t aExcludeFlags, + nsACString& aPermissionString); + + // FIXME-jsplugins comment about fake + bool HavePluginForType(const nsACString& aMimeType, + PluginFilter aFilter = eExcludeDisabled); + + void GetPlugins(nsTArray<nsCOMPtr<nsIInternalPluginTag>>& aPluginArray, + bool aIncludeDisabled = false); + + /** + * Returns true if a plugin can be used to load the requested MIME type. Used + * for short circuiting before sending things to plugin code. + */ + static bool CanUsePluginForMIMEType(const nsACString& aMIMEType); + + // checks whether aType is a type we recognize for potential special handling + enum SpecialType { + eSpecialType_None, + // Needed to whitelist for async init support + eSpecialType_Test, + // Informs some decisions about OOP and quirks + eSpecialType_Flash + }; + static SpecialType GetSpecialType(const nsACString& aMIMEType); + + private: + // Find a plugin for the given type. If aIncludeFake is true a fake plugin + // will be preferred if one exists; otherwise a fake plugin will never be + // returned. If aCheckEnabled is false, disabled plugins can be returned. + nsIInternalPluginTag* FindPluginForType(const nsACString& aMimeType, + bool aIncludeFake, + bool aCheckEnabled); + + // Find specifically a fake plugin for the given type. If aCheckEnabled is + // false, disabled plugins can be returned. + nsFakePluginTag* FindFakePluginForType(const nsACString& aMimeType, + bool aCheckEnabled); + + // Find specifically a fake plugin for the given extension. If aCheckEnabled + // is false, disabled plugins can be returned. aMimeType will be filled in + // with the MIME type the plugin is registered for. + nsFakePluginTag* FindFakePluginForExtension(const nsACString& aExtension, + /* out */ nsACString& aMimeType, + bool aCheckEnabled); + + // Checks to see if a tag object is in our list of live tags. + bool IsLiveTag(nsIPluginTag* tag); + + // To be used by the chrome process whenever the set of plugins changes. + void IncrementChromeEpoch(); + + // To be used by the chrome process; returns the current epoch. + uint32_t ChromeEpoch(); + + // To be used by the content process to get/set the last observed epoch value + // from the chrome process. + uint32_t ChromeEpochForContent(); + void SetChromeEpochForContent(uint32_t aEpoch); + + nsTArray<RefPtr<nsFakePluginTag>> mFakePlugins; + + // This epoch increases each time we load the list of plugins from disk. + // In the chrome process, this stores the actual epoch. + // In the content process, this stores the last epoch value observed + // when reading plugins from chrome. + uint32_t mPluginEpoch; + + // We need to hold a global ptr to ourselves because we register for + // two different CIDs for some reason... + static mozilla::StaticRefPtr<nsPluginHost> sInst; +}; + +#endif // nsPluginHost_h_ diff --git a/dom/plugins/base/nsPluginLogging.h b/dom/plugins/base/nsPluginLogging.h new file mode 100644 index 0000000000..029a073308 --- /dev/null +++ b/dom/plugins/base/nsPluginLogging.h @@ -0,0 +1,54 @@ +/* -*- Mode: C++; tab-width: 4; 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/. */ + +/* Plugin Module Logging usage instructions and includes */ +//////////////////////////////////////////////////////////////////////////////// +#ifndef nsPluginLogging_h__ +#define nsPluginLogging_h__ + +#include "mozilla/Logging.h" + +//////////////////////////////////////////////////////////////////////////////// +// Basic Plugin Logging Usage Instructions +// +// 1. Set this environment variable: MOZ_LOG=<name>:<level> + +// Choose the <name> and <level> from this list (no quotes): + +// Log Names <name> +#define NPN_LOG_NAME "PluginNPN" +#define NPP_LOG_NAME "PluginNPP" +#define PLUGIN_LOG_NAME "Plugin" + +// Levels <level> +#define PLUGIN_LOG_ALWAYS mozilla::LogLevel::Error +#define PLUGIN_LOG_BASIC mozilla::LogLevel::Info +#define PLUGIN_LOG_NORMAL mozilla::LogLevel::Debug +#define PLUGIN_LOG_NOISY mozilla::LogLevel::Verbose + +// 2. You can combine logs and levels by separating them with a comma: +// My favorite Win32 Example: SET MOZ_LOG=Plugin:5,PluginNPP:5,PluginNPN:5 + +// 3. Instead of output going to the console, you can log to a file. +// Additionally, set the MOZ_LOG_FILE environment variable to point to the +// full path of a file. +// My favorite Win32 Example: SET MOZ_LOG_FILE=c:\temp\pluginLog.txt + +// 4. For complete information see the Gecko Developer guide: +// https://firefox-source-docs.mozilla.org/xpcom/logging.html + +class nsPluginLogging { + public: + static mozilla::LazyLogModule gNPNLog; // 4.x NP API, calls into navigator + static mozilla::LazyLogModule gNPPLog; // 4.x NP API, calls into plugin + static mozilla::LazyLogModule gPluginLog; // general plugin log +}; + +// Quick-use macros +#define NPN_PLUGIN_LOG(a, b) MOZ_LOG(nsPluginLogging::gNPNLog, a, b) +#define NPP_PLUGIN_LOG(a, b) MOZ_LOG(nsPluginLogging::gNPPLog, a, b) +#define PLUGIN_LOG(a, b) MOZ_LOG(nsPluginLogging::gPluginLog, a, b) + +#endif // nsPluginLogging_h__ diff --git a/dom/plugins/base/nsPluginTags.cpp b/dom/plugins/base/nsPluginTags.cpp new file mode 100644 index 0000000000..37e9f742ac --- /dev/null +++ b/dom/plugins/base/nsPluginTags.cpp @@ -0,0 +1,391 @@ +/* -*- 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 <cctype> +#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<nsCString>& aExtensions, + const nsTArray<nsCString>& 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<nsIPrefBranch> 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<nsCString>& aMimeTypes, + const nsTArray<nsCString>& aMimeDescriptions, + const nsTArray<nsCString>& 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<nsIURI>&& aHandlerURI, + const char* aName, const char* aDescription, + const nsTArray<nsCString>& aMimeTypes, + const nsTArray<nsCString>& aMimeDescriptions, + const nsTArray<nsCString>& 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<nsFakePluginTag> 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<nsCString>& aResults) { + aResults = mMimeTypes.Clone(); + return NS_OK; +} + +NS_IMETHODIMP +nsFakePluginTag::GetMimeDescriptions(nsTArray<nsCString>& aResults) { + aResults = mMimeDescriptions.Clone(); + return NS_OK; +} + +NS_IMETHODIMP +nsFakePluginTag::GetExtensions(nsTArray<nsCString>& 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; +} diff --git a/dom/plugins/base/nsPluginTags.h b/dom/plugins/base/nsPluginTags.h new file mode 100644 index 0000000000..2219112d26 --- /dev/null +++ b/dom/plugins/base/nsPluginTags.h @@ -0,0 +1,149 @@ +/* -*- 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/. */ + +#ifndef nsPluginTags_h_ +#define nsPluginTags_h_ + +#include "mozilla/Attributes.h" +#include "nscore.h" +#include "nsCOMPtr.h" +#include "nsCOMArray.h" +#include "nsIPluginTag.h" +#include "nsITimer.h" +#include "nsString.h" + +class nsIURI; + +namespace mozilla::dom { +struct FakePluginTagInit; +} // namespace mozilla::dom + +// An interface representing plugin tags internally. +#define NS_IINTERNALPLUGINTAG_IID \ + { \ + 0xe8fdd227, 0x27da, 0x46ee, { \ + 0xbe, 0xf3, 0x1a, 0xef, 0x5a, 0x8f, 0xc5, 0xb4 \ + } \ + } + +class nsIInternalPluginTag : public nsIPluginTag { + public: + NS_DECLARE_STATIC_IID_ACCESSOR(NS_IINTERNALPLUGINTAG_IID) + + nsIInternalPluginTag(); + nsIInternalPluginTag(const char* aName, const char* aDescription, + const char* aFileName, const char* aVersion); + nsIInternalPluginTag(const char* aName, const char* aDescription, + const char* aFileName, const char* aVersion, + const nsTArray<nsCString>& aMimeTypes, + const nsTArray<nsCString>& aMimeDescriptions, + const nsTArray<nsCString>& aExtensions); + + virtual bool IsEnabled() = 0; + virtual const nsCString& GetNiceFileName() = 0; + + const nsCString& Name() const { return mName; } + const nsCString& Description() const { return mDescription; } + + const nsTArray<nsCString>& MimeTypes() const { return mMimeTypes; } + + const nsTArray<nsCString>& MimeDescriptions() const { + return mMimeDescriptions; + } + + const nsTArray<nsCString>& Extensions() const { return mExtensions; } + + const nsCString& FileName() const { return mFileName; } + + const nsCString& Version() const { return mVersion; } + + // Returns true if this plugin claims it supports this MIME type. The + // comparison is done ASCII-case-insensitively. + bool HasMimeType(const nsACString& aMimeType) const; + + // Returns true if this plugin claims it supports the given extension. In + // that case, aMatchingType is set to the MIME type the plugin claims + // corresponds to this extension. The match on aExtension is done + // ASCII-case-insensitively. + bool HasExtension(const nsACString& aExtension, + /* out */ nsACString& aMatchingType) const; + + // These must match the STATE_* values in nsIPluginTag.idl + enum PluginState { + ePluginState_Disabled = 0, + ePluginState_Clicktoplay = 1, + ePluginState_Enabled = 2, + ePluginState_MaxValue = 3, + }; + + protected: + ~nsIInternalPluginTag(); + + nsCString mName; // UTF-8 + nsCString mDescription; // UTF-8 + nsCString mFileName; // UTF-8 + nsCString mVersion; // UTF-8 + nsTArray<nsCString> mMimeTypes; // UTF-8 + nsTArray<nsCString> mMimeDescriptions; // UTF-8 + nsTArray<nsCString> mExtensions; // UTF-8 + + static uint32_t sNextId; +}; +NS_DEFINE_STATIC_IID_ACCESSOR(nsIInternalPluginTag, NS_IINTERNALPLUGINTAG_IID) + +// A class representing "fake" plugin tags for Javascript-based plugins. +// There are currently no other types of supported plugins. +class nsFakePluginTag : public nsIInternalPluginTag, public nsIFakePluginTag { + public: + NS_DECL_ISUPPORTS + NS_DECL_NSIPLUGINTAG + NS_DECL_NSIFAKEPLUGINTAG + + static nsresult Create(const mozilla::dom::FakePluginTagInit& aInitDictionary, + nsFakePluginTag** aPluginTag); + nsFakePluginTag(uint32_t aId, already_AddRefed<nsIURI>&& aHandlerURI, + const char* aName, const char* aDescription, + const nsTArray<nsCString>& aMimeTypes, + const nsTArray<nsCString>& aMimeDescriptions, + const nsTArray<nsCString>& aExtensions, + const nsCString& aNiceName, const nsString& aSandboxScript); + + bool IsEnabled() override; + const nsCString& GetNiceFileName() override; + + bool HandlerURIMatches(nsIURI* aURI); + + nsIURI* HandlerURI() const { return mHandlerURI; } + + uint32_t Id() const { return mId; } + + const nsString& SandboxScript() const { return mSandboxScript; } + + static const int32_t NOT_JSPLUGIN = -1; + + private: + nsFakePluginTag(); + virtual ~nsFakePluginTag(); + + // A unique id for this JS-implemented plugin. Registering a plugin through + // nsPluginHost::RegisterFakePlugin assigns a new id. The id is transferred + // through IPC when getting the list of JS-implemented plugins from child + // processes, so it should be consistent across processes. + // 0 is a valid id. + uint32_t mId; + + // The URI of the handler for our fake plugin. + // FIXME-jsplugins do we need to sanity check these? + nsCOMPtr<nsIURI> mHandlerURI; + + nsCString mFullPath; + nsCString mNiceName; + + nsString mSandboxScript; + + PluginState mState; +}; + +#endif // nsPluginTags_h_ diff --git a/dom/plugins/test/crashtests/110650-1.html b/dom/plugins/test/crashtests/110650-1.html new file mode 100644 index 0000000000..9826227b03 --- /dev/null +++ b/dom/plugins/test/crashtests/110650-1.html @@ -0,0 +1,11 @@ +<HTML> +<HEAD> +<TITLE>123246 testcase</TITLE> +</HEAD> +<BODY> +<object align="right" width=100> +</object> +</BODY > +</HTML> + + diff --git a/dom/plugins/test/crashtests/41276-1.html b/dom/plugins/test/crashtests/41276-1.html new file mode 100644 index 0000000000..ba35c34fa0 --- /dev/null +++ b/dom/plugins/test/crashtests/41276-1.html @@ -0,0 +1,28 @@ +<HTML><HEAD><TITLE>Plugin Limit</TITLE></HEAD>
+<BODY>
+
+Mozilla has a hardcoded limit of 10 simultaneously embedded plugins.<br>
+If that limit is exceeded, plugin instances are prematurely destroyed (see the empty boxes below).<br>
+Leave or reload a page that has a prematurely destroyed plugin and mozilla will crash.<br>
+Sometimes, just loading this page will cause mozilla to crash.<br>
+
+
+<br>
+<embed TYPE=audio/x-pn-realaudio-plugin WIDTH=300 HEIGHT=75 CONTROLS="Default" AUTOSTART="FALSE" CONSOLE="RA_Player">
+<embed TYPE=audio/x-pn-realaudio-plugin WIDTH=300 HEIGHT=75 CONTROLS="Default" AUTOSTART="FALSE" CONSOLE="RA_Player"><br>
+<embed TYPE=audio/x-pn-realaudio-plugin WIDTH=300 HEIGHT=75 CONTROLS="Default" AUTOSTART="FALSE" CONSOLE="RA_Player">
+<embed TYPE=audio/x-pn-realaudio-plugin WIDTH=300 HEIGHT=75 CONTROLS="Default" AUTOSTART="FALSE" CONSOLE="RA_Player"><br>
+<embed TYPE=audio/x-pn-realaudio-plugin WIDTH=300 HEIGHT=75 CONTROLS="Default" AUTOSTART="FALSE" CONSOLE="RA_Player">
+<embed TYPE=audio/x-pn-realaudio-plugin WIDTH=300 HEIGHT=75 CONTROLS="Default" AUTOSTART="FALSE" CONSOLE="RA_Player"><br>
+<embed TYPE=audio/x-pn-realaudio-plugin WIDTH=300 HEIGHT=75 CONTROLS="Default" AUTOSTART="FALSE" CONSOLE="RA_Player">
+<embed TYPE=audio/x-pn-realaudio-plugin WIDTH=300 HEIGHT=75 CONTROLS="Default" AUTOSTART="FALSE" CONSOLE="RA_Player"><br>
+<embed TYPE=audio/x-pn-realaudio-plugin WIDTH=300 HEIGHT=75 CONTROLS="Default" AUTOSTART="FALSE" CONSOLE="RA_Player">
+<embed TYPE=audio/x-pn-realaudio-plugin WIDTH=300 HEIGHT=75 CONTROLS="Default" AUTOSTART="FALSE" CONSOLE="RA_Player"><br>
+<embed TYPE=audio/x-pn-realaudio-plugin WIDTH=300 HEIGHT=75 CONTROLS="Default" AUTOSTART="FALSE" CONSOLE="RA_Player">
+<embed TYPE=audio/x-pn-realaudio-plugin WIDTH=300 HEIGHT=75 CONTROLS="Default" AUTOSTART="FALSE" CONSOLE="RA_Player"><br>
+<embed TYPE=audio/x-pn-realaudio-plugin WIDTH=300 HEIGHT=75 CONTROLS="Default" AUTOSTART="FALSE" CONSOLE="RA_Player">
+<embed TYPE=audio/x-pn-realaudio-plugin WIDTH=300 HEIGHT=75 CONTROLS="Default" AUTOSTART="FALSE" CONSOLE="RA_Player"><br>
+<embed TYPE=audio/x-pn-realaudio-plugin WIDTH=300 HEIGHT=75 CONTROLS="Default" AUTOSTART="FALSE" CONSOLE="RA_Player">
+<embed TYPE=audio/x-pn-realaudio-plugin WIDTH=300 HEIGHT=75 CONTROLS="Default" AUTOSTART="FALSE" CONSOLE="RA_Player"><br>
+<br>
+</BODY></HTML>
diff --git a/dom/plugins/test/crashtests/48856-1.html b/dom/plugins/test/crashtests/48856-1.html new file mode 100644 index 0000000000..cd0de2ab94 --- /dev/null +++ b/dom/plugins/test/crashtests/48856-1.html @@ -0,0 +1,10 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" + "http://www.w3.org/TR/REC-html40/loose.dtd"> +<HTML> + <HEAD> + <TITLE>Mozilla Bug 48856</TITLE> + </HEAD> + <BODY> + <EMBED></EMBED> + </BODY> +</HTML> diff --git a/dom/plugins/test/crashtests/752340.html b/dom/plugins/test/crashtests/752340.html new file mode 100644 index 0000000000..c4c8c464f5 --- /dev/null +++ b/dom/plugins/test/crashtests/752340.html @@ -0,0 +1,19 @@ +<!DOCTYPE HTML> +<html> +<head> + <script type="text/javascript"> + // Failures in this file can manifest as ###!!! ASSERTION: scope has non-empty map: '0 == mWrappedNativeMap->Count()' + // followed by an Assertion failure: allocated() crash during the next GC. + // It can also manifest as a leak. + function breakthings() { + var e = document.createElement("embed"); + var i = document.getElementById("i"); + i.contentDocument.body.appendChild(e); + i.src = "about:blank"; + } + </script> +</head> +<body onload="javascript:breakthings();"> +<iframe id="i" /> +</body> +</html> diff --git a/dom/plugins/test/crashtests/843086.xhtml b/dom/plugins/test/crashtests/843086.xhtml new file mode 100644 index 0000000000..a88e2193fc --- /dev/null +++ b/dom/plugins/test/crashtests/843086.xhtml @@ -0,0 +1 @@ +<applet xmlns="http://www.w3.org/1999/xhtml" /> diff --git a/dom/plugins/test/crashtests/crashtests.list b/dom/plugins/test/crashtests/crashtests.list new file mode 100644 index 0000000000..568cd8fca5 --- /dev/null +++ b/dom/plugins/test/crashtests/crashtests.list @@ -0,0 +1,5 @@ +HTTP load 41276-1.html +HTTP load 48856-1.html +HTTP load 110650-1.html +HTTP load 752340.html +HTTP load 843086.xhtml diff --git a/dom/plugins/test/mochitest/block_all_plugins.html b/dom/plugins/test/mochitest/block_all_plugins.html new file mode 100644 index 0000000000..3ccdda1373 --- /dev/null +++ b/dom/plugins/test/mochitest/block_all_plugins.html @@ -0,0 +1,10 @@ +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +</head> +<body> + <object id="object" type="application/x-shockwave-flash"></object> + <embed id="embed" type="application/x-shockwave-flash"></embed> +</body> +</html> diff --git a/dom/plugins/test/mochitest/browser.ini b/dom/plugins/test/mochitest/browser.ini new file mode 100644 index 0000000000..d5190abce7 --- /dev/null +++ b/dom/plugins/test/mochitest/browser.ini @@ -0,0 +1,5 @@ +[DEFAULT] +support-files = + block_all_plugins.html + +[browser_blockallplugins.js] diff --git a/dom/plugins/test/mochitest/browser_blockallplugins.js b/dom/plugins/test/mochitest/browser_blockallplugins.js new file mode 100644 index 0000000000..346db9d949 --- /dev/null +++ b/dom/plugins/test/mochitest/browser_blockallplugins.js @@ -0,0 +1,64 @@ +var gTestRoot = getRootDirectory(gTestPath).replace( + "chrome://mochitests/content/", + "http://127.0.0.1:8888/" +); + +add_task(async function () { + registerCleanupFunction(function () { + gBrowser.removeCurrentTab(); + window.focus(); + }); +}); + +// simple tab load helper, pilfered from browser plugin tests +function promiseTabLoadEvent(tab, url) { + info("Wait tab event: load"); + + function handle(loadedUrl) { + if (loadedUrl === "about:blank" || (url && loadedUrl !== url)) { + info(`Skipping spurious load event for ${loadedUrl}`); + return false; + } + + info("Tab event received: load"); + return true; + } + + let loaded = BrowserTestUtils.browserLoaded(tab.linkedBrowser, false, handle); + + if (url) { + BrowserTestUtils.loadURIString(tab.linkedBrowser, url); + } + + return loaded; +} + +add_task(async function () { + let pluginTab = (gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser)); + await promiseTabLoadEvent(pluginTab, gTestRoot + "block_all_plugins.html"); + await SpecialPowers.spawn(gBrowser.selectedBrowser, [], async function () { + let doc = content.document; + + let objectElt = doc.getElementById("object"); + Assert.ok(!!objectElt, "object should exist"); + Assert.ok( + objectElt instanceof Ci.nsIObjectLoadingContent, + "object should be an nsIObjectLoadingContent" + ); + Assert.ok( + objectElt.displayedType == Ci.nsIObjectLoadingContent.TYPE_FALLBACK, + "object should be blocked" + ); + + let embedElt = doc.getElementById("embed"); + Assert.ok(!!embedElt, "embed should exist"); + Assert.ok( + embedElt instanceof Ci.nsIObjectLoadingContent, + "embed should be an nsIObjectLoadingContent" + ); + Assert.ok( + embedElt.displayedType == Ci.nsIObjectLoadingContent.TYPE_FALLBACK, + "embed should be blocked" + ); + }); +}); diff --git a/dom/plugins/test/mochitest/large-pic.jpg b/dom/plugins/test/mochitest/large-pic.jpg Binary files differnew file mode 100644 index 0000000000..b167f6b9ba --- /dev/null +++ b/dom/plugins/test/mochitest/large-pic.jpg diff --git a/dom/plugins/test/mochitest/mixed_case_mime.sjs b/dom/plugins/test/mochitest/mixed_case_mime.sjs new file mode 100644 index 0000000000..57b48b4b96 --- /dev/null +++ b/dom/plugins/test/mochitest/mixed_case_mime.sjs @@ -0,0 +1,7 @@ +function handleRequest(request, response) { + response.processAsync(); + response.setHeader("Content-Type", "image/pNG", false); + + response.write("Hello world.\n"); + response.finish(); +} diff --git a/dom/plugins/test/mochitest/mochitest.ini b/dom/plugins/test/mochitest/mochitest.ini new file mode 100644 index 0000000000..d4b4940ef1 --- /dev/null +++ b/dom/plugins/test/mochitest/mochitest.ini @@ -0,0 +1,10 @@ +[DEFAULT] +skip-if = headless # crash on shutdown, no other failures +support-files = + large-pic.jpg + mixed_case_mime.sjs + +[test_mixed_case_mime.html] +skip-if = (processor == 'aarch64' && os == 'win') +reason = Plugins are not supported on Windows/AArch64 +[test_plugin_fallback_focus.html] diff --git a/dom/plugins/test/mochitest/test_mixed_case_mime.html b/dom/plugins/test/mochitest/test_mixed_case_mime.html new file mode 100644 index 0000000000..fac5e386f1 --- /dev/null +++ b/dom/plugins/test/mochitest/test_mixed_case_mime.html @@ -0,0 +1,25 @@ +<body> +<head> + <title>Test mixed case mimetype for plugins</title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +<script> + SimpleTest.expectAssertions(0, 1); + + SimpleTest.waitForExplicitFinish(); + + function frameLoaded() { + var contentDocument = document.getElementById("testframe").contentDocument; + ok(contentDocument.body.innerHTML.length, "Frame content shouldn't be empty."); + ok(contentDocument.images.length, "Frame content should have an image."); + SimpleTest.finish(); + } +</script> +</head> +<body> + <p id="display"></p> + + <iframe id="testframe" name="testframe" onload="frameLoaded()" src="mixed_case_mime.sjs"></iframe> + +</body> +</html> diff --git a/dom/plugins/test/mochitest/test_plugin_fallback_focus.html b/dom/plugins/test/mochitest/test_plugin_fallback_focus.html new file mode 100644 index 0000000000..e89abb44df --- /dev/null +++ b/dom/plugins/test/mochitest/test_plugin_fallback_focus.html @@ -0,0 +1,80 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset="utf-8"> + <title>Test that plugins reject focus</title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <script src="/tests/SimpleTest/EventUtils.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> +</head> +<body> +<div id="content"> + <object id="obj_elt" type="application/x-shockwave-flash"></object> + <object tabindex='0' id="obj_elt_with_idx" type="application/x-shockwave-flash"></object> + <embed id="embed_elt" type="application/x-shockwave-flash"></embed> +</div> +<script type="application/javascript"> +var objElt = document.getElementById('obj_elt'); +var objEltWithIdx = document.getElementById('obj_elt_with_idx'); +var embedElt = document.getElementById('embed_elt'); + +function checkHasFocus(expected, typeOfElts, elt) { + ok((document.activeElement == elt) == expected, + typeOfElts + " element should " + (expected ? "" : "not ") + "accept focus"); +} + +function checkNoneHasFocus(typeOfElts) { + checkHasFocus(false, typeOfElts + " <object>", objElt); + checkHasFocus(false, typeOfElts + " <object> with tabindex", objEltWithIdx); + checkHasFocus(false, typeOfElts + " <embed>", embedElt); +} + +function checkFocusable(expected, typeOfElts, elt) { + elt.focus(); + checkHasFocus(expected, typeOfElts, elt); +} + +// As plugins, object and embed elements are not given focus +ok(objElt != null, "object element should exist"); +ok(objEltWithIdx != null, "object element with tabindex should exist"); +ok(embedElt != null, "embed element should exist"); + +// As plugins, obj/embed_elt can not take focus +checkNoneHasFocus("plugin"); + +// Switch obj/embed_elt attributes from plugin to image +objElt.data = "large-pic.jpg"; +objElt.width = 100; +objElt.height = 100; +objElt.type = "image/jpg"; +objEltWithIdx.data = "large-pic.jpg"; +objEltWithIdx.width = 100; +objEltWithIdx.height = 100; +objEltWithIdx.type = "image/jpg"; +embedElt.src = "large-pic.jpg"; +embedElt.width = 100; +embedElt.height = 100; +embedElt.type = "image/jpg"; + +// As images, obj/embed_elt can take focus as image +// object image elements require a tabindex to accept focus. +// embed elements must be reparented before new type is recognized. +checkFocusable(false, "<object> image", objElt); +checkFocusable(true, "<object> image with tabindex", objEltWithIdx); +checkFocusable(false, "<embed> plugin with image attribs before reparenting", embedElt); +embedElt.parentNode.appendChild(embedElt); +checkFocusable(true, "<embed> image", embedElt); + +// Switch obj/embed_elt attributes from image to plugin +objElt.type = "application/x-shockwave-flash"; +embedElt.type = "application/x-shockwave-flash"; + +// embed elements must be reparented before new type is recognized. +checkFocusable(true, "<embed> image with plugin attribs", embedElt); +embedElt.parentNode.appendChild(embedElt); +checkNoneHasFocus("plugin"); +</script> +</body> +</html> + + diff --git a/dom/plugins/test/moz.build b/dom/plugins/test/moz.build new file mode 100644 index 0000000000..c3f85d5c48 --- /dev/null +++ b/dom/plugins/test/moz.build @@ -0,0 +1,9 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# 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/. + +if CONFIG["MOZ_WIDGET_TOOLKIT"] in ("gtk", "cocoa", "windows"): + MOCHITEST_MANIFESTS += ["mochitest/mochitest.ini"] + BROWSER_CHROME_MANIFESTS += ["mochitest/browser.ini"] |