summaryrefslogtreecommitdiffstats
path: root/dom/base/nsPluginArray.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'dom/base/nsPluginArray.cpp')
-rw-r--r--dom/base/nsPluginArray.cpp553
1 files changed, 553 insertions, 0 deletions
diff --git a/dom/base/nsPluginArray.cpp b/dom/base/nsPluginArray.cpp
new file mode 100644
index 000000000..b9c946ca3
--- /dev/null
+++ b/dom/base/nsPluginArray.cpp
@@ -0,0 +1,553 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 "nsPluginArray.h"
+
+#include "mozilla/dom/PluginArrayBinding.h"
+#include "mozilla/dom/PluginBinding.h"
+#include "mozilla/dom/HiddenPluginEvent.h"
+
+#include "nsMimeTypeArray.h"
+#include "Navigator.h"
+#include "nsIDocShell.h"
+#include "nsIWebNavigation.h"
+#include "nsPluginHost.h"
+#include "nsPluginTags.h"
+#include "nsIObserverService.h"
+#include "nsIWeakReference.h"
+#include "mozilla/Services.h"
+#include "nsIInterfaceRequestorUtils.h"
+#include "nsContentUtils.h"
+#include "nsIPermissionManager.h"
+#include "nsIDocument.h"
+#include "nsIBlocklistService.h"
+
+using namespace mozilla;
+using namespace mozilla::dom;
+
+nsPluginArray::nsPluginArray(nsPIDOMWindowInner* aWindow)
+ : mWindow(aWindow)
+{
+}
+
+void
+nsPluginArray::Init()
+{
+ nsCOMPtr<nsIObserverService> obsService =
+ mozilla::services::GetObserverService();
+ if (obsService) {
+ obsService->AddObserver(this, "plugin-info-updated", true);
+ }
+}
+
+nsPluginArray::~nsPluginArray()
+{
+}
+
+static bool
+ResistFingerprinting() {
+ return !nsContentUtils::ThreadsafeIsCallerChrome() &&
+ nsContentUtils::ResistFingerprinting();
+}
+
+nsPIDOMWindowInner*
+nsPluginArray::GetParentObject() const
+{
+ MOZ_ASSERT(mWindow);
+ return mWindow;
+}
+
+JSObject*
+nsPluginArray::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
+{
+ return PluginArrayBinding::Wrap(aCx, this, aGivenProto);
+}
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(nsPluginArray)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(nsPluginArray)
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsPluginArray)
+ NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
+ NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIObserver)
+ NS_INTERFACE_MAP_ENTRY(nsIObserver)
+ NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
+NS_INTERFACE_MAP_END
+
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(nsPluginArray,
+ mWindow,
+ mPlugins,
+ mCTPPlugins)
+
+static void
+GetPluginMimeTypes(const nsTArray<RefPtr<nsPluginElement> >& aPlugins,
+ nsTArray<RefPtr<nsMimeType> >& aMimeTypes)
+{
+ for (uint32_t i = 0; i < aPlugins.Length(); ++i) {
+ nsPluginElement *plugin = aPlugins[i];
+ aMimeTypes.AppendElements(plugin->MimeTypes());
+ }
+}
+
+static bool
+operator<(const RefPtr<nsMimeType>& lhs, const RefPtr<nsMimeType>& rhs)
+{
+ // Sort MIME types alphabetically by type name.
+ return lhs->Type() < rhs->Type();
+}
+
+void
+nsPluginArray::GetMimeTypes(nsTArray<RefPtr<nsMimeType>>& aMimeTypes)
+{
+ aMimeTypes.Clear();
+
+ if (!AllowPlugins()) {
+ return;
+ }
+
+ EnsurePlugins();
+
+ GetPluginMimeTypes(mPlugins, aMimeTypes);
+
+ // Alphabetize the enumeration order of non-hidden MIME types to reduce
+ // fingerprintable entropy based on plugins' installation file times.
+ aMimeTypes.Sort();
+}
+
+void
+nsPluginArray::GetCTPMimeTypes(nsTArray<RefPtr<nsMimeType>>& aMimeTypes)
+{
+ aMimeTypes.Clear();
+
+ if (!AllowPlugins()) {
+ return;
+ }
+
+ EnsurePlugins();
+
+ GetPluginMimeTypes(mCTPPlugins, aMimeTypes);
+
+ // Alphabetize the enumeration order of non-hidden MIME types to reduce
+ // fingerprintable entropy based on plugins' installation file times.
+ aMimeTypes.Sort();
+}
+
+nsPluginElement*
+nsPluginArray::Item(uint32_t aIndex)
+{
+ bool unused;
+ return IndexedGetter(aIndex, unused);
+}
+
+nsPluginElement*
+nsPluginArray::NamedItem(const nsAString& aName)
+{
+ bool unused;
+ return NamedGetter(aName, unused);
+}
+
+void
+nsPluginArray::Refresh(bool aReloadDocuments)
+{
+ RefPtr<nsPluginHost> pluginHost = nsPluginHost::GetInst();
+
+ if(!AllowPlugins() || !pluginHost) {
+ return;
+ }
+
+ // NS_ERROR_PLUGINS_PLUGINSNOTCHANGED on reloading plugins indicates
+ // that plugins did not change and was not reloaded
+ if (pluginHost->ReloadPlugins() ==
+ NS_ERROR_PLUGINS_PLUGINSNOTCHANGED) {
+ nsTArray<nsCOMPtr<nsIInternalPluginTag> > newPluginTags;
+ pluginHost->GetPlugins(newPluginTags);
+
+ // Check if the number of plugins we know about are different from
+ // the number of plugin tags the plugin host knows about. If the
+ // lengths are different, we refresh. This is safe because we're
+ // notified for every plugin enabling/disabling event that
+ // happens, and therefore the lengths will be in sync only when
+ // the both arrays contain the same plugin tags (though as
+ // different types).
+ if (newPluginTags.Length() == mPlugins.Length()) {
+ return;
+ }
+ }
+
+ mPlugins.Clear();
+ mCTPPlugins.Clear();
+
+ nsCOMPtr<nsIDOMNavigator> navigator = mWindow->GetNavigator();
+
+ if (!navigator) {
+ return;
+ }
+
+ static_cast<mozilla::dom::Navigator*>(navigator.get())->RefreshMIMEArray();
+
+ nsCOMPtr<nsIWebNavigation> webNav = do_GetInterface(mWindow);
+ if (aReloadDocuments && webNav) {
+ webNav->Reload(nsIWebNavigation::LOAD_FLAGS_NONE);
+ }
+}
+
+nsPluginElement*
+nsPluginArray::IndexedGetter(uint32_t aIndex, bool &aFound)
+{
+ aFound = false;
+
+ if (!AllowPlugins() || ResistFingerprinting()) {
+ return nullptr;
+ }
+
+ EnsurePlugins();
+
+ aFound = aIndex < mPlugins.Length();
+
+ if (!aFound) {
+ return nullptr;
+ }
+
+ return mPlugins[aIndex];
+}
+
+void
+nsPluginArray::Invalidate()
+{
+ nsCOMPtr<nsIObserverService> obsService =
+ mozilla::services::GetObserverService();
+ if (obsService) {
+ obsService->RemoveObserver(this, "plugin-info-updated");
+ }
+}
+
+static nsPluginElement*
+FindPlugin(const nsTArray<RefPtr<nsPluginElement> >& aPlugins,
+ const nsAString& aName)
+{
+ for (uint32_t i = 0; i < aPlugins.Length(); ++i) {
+ nsAutoString pluginName;
+ nsPluginElement* plugin = aPlugins[i];
+ plugin->GetName(pluginName);
+
+ if (pluginName.Equals(aName)) {
+ return plugin;
+ }
+ }
+
+ return nullptr;
+}
+
+nsPluginElement*
+nsPluginArray::NamedGetter(const nsAString& aName, bool &aFound)
+{
+ aFound = false;
+
+ if (!AllowPlugins() || ResistFingerprinting()) {
+ return nullptr;
+ }
+
+ EnsurePlugins();
+
+ nsPluginElement* plugin = FindPlugin(mPlugins, aName);
+ aFound = (plugin != nullptr);
+ if (!aFound) {
+ nsPluginElement* hiddenPlugin = FindPlugin(mCTPPlugins, aName);
+ if (hiddenPlugin) {
+ NotifyHiddenPluginTouched(hiddenPlugin);
+ }
+ }
+ return plugin;
+}
+
+void nsPluginArray::NotifyHiddenPluginTouched(nsPluginElement* aHiddenElement)
+{
+ HiddenPluginEventInit init;
+ init.mTag = aHiddenElement->PluginTag();
+ nsCOMPtr<nsIDocument> doc = aHiddenElement->GetParentObject()->GetDoc();
+ RefPtr<HiddenPluginEvent> event =
+ HiddenPluginEvent::Constructor(doc, NS_LITERAL_STRING("HiddenPlugin"), init);
+ event->SetTarget(doc);
+ event->SetTrusted(true);
+ event->WidgetEventPtr()->mFlags.mOnlyChromeDispatch = true;
+ bool dummy;
+ doc->DispatchEvent(event, &dummy);
+}
+
+uint32_t
+nsPluginArray::Length()
+{
+ if (!AllowPlugins() || ResistFingerprinting()) {
+ return 0;
+ }
+
+ EnsurePlugins();
+
+ return mPlugins.Length();
+}
+
+void
+nsPluginArray::GetSupportedNames(nsTArray<nsString>& aRetval)
+{
+ aRetval.Clear();
+
+ if (!AllowPlugins()) {
+ return;
+ }
+
+ for (uint32_t i = 0; i < mPlugins.Length(); ++i) {
+ nsAutoString pluginName;
+ mPlugins[i]->GetName(pluginName);
+
+ aRetval.AppendElement(pluginName);
+ }
+}
+
+NS_IMETHODIMP
+nsPluginArray::Observe(nsISupports *aSubject, const char *aTopic,
+ const char16_t *aData) {
+ if (!nsCRT::strcmp(aTopic, "plugin-info-updated")) {
+ Refresh(false);
+ }
+
+ return NS_OK;
+}
+
+bool
+nsPluginArray::AllowPlugins() const
+{
+ nsCOMPtr<nsIDocShell> docShell = mWindow ? mWindow->GetDocShell() : nullptr;
+
+ return docShell && docShell->PluginsAllowedInCurrentDoc();
+}
+
+static bool
+operator<(const RefPtr<nsPluginElement>& lhs,
+ const RefPtr<nsPluginElement>& rhs)
+{
+ // Sort plugins alphabetically by name.
+ return lhs->PluginTag()->Name() < rhs->PluginTag()->Name();
+}
+
+static bool
+PluginShouldBeHidden(nsCString aName) {
+ // This only supports one hidden plugin
+ return Preferences::GetCString("plugins.navigator.hidden_ctp_plugin").Equals(aName);
+}
+
+void
+nsPluginArray::EnsurePlugins()
+{
+ if (!mPlugins.IsEmpty() || !mCTPPlugins.IsEmpty()) {
+ // We already have an array of plugin elements.
+ return;
+ }
+
+ RefPtr<nsPluginHost> pluginHost = nsPluginHost::GetInst();
+ if (!pluginHost) {
+ // We have no plugin host.
+ return;
+ }
+
+ nsTArray<nsCOMPtr<nsIInternalPluginTag> > pluginTags;
+ pluginHost->GetPlugins(pluginTags);
+
+ // need to wrap each of these with a nsPluginElement, which is
+ // scriptable.
+ for (uint32_t i = 0; i < pluginTags.Length(); ++i) {
+ nsCOMPtr<nsPluginTag> pluginTag = do_QueryInterface(pluginTags[i]);
+ if (!pluginTag) {
+ mPlugins.AppendElement(new nsPluginElement(mWindow, pluginTags[i]));
+ } else if (pluginTag->IsActive()) {
+ uint32_t permission = nsIPermissionManager::ALLOW_ACTION;
+ uint32_t blocklistState;
+ if (pluginTag->IsClicktoplay() &&
+ NS_SUCCEEDED(pluginTag->GetBlocklistState(&blocklistState)) &&
+ blocklistState == nsIBlocklistService::STATE_NOT_BLOCKED) {
+ nsCString name;
+ pluginTag->GetName(name);
+ if (PluginShouldBeHidden(name)) {
+ RefPtr<nsPluginHost> pluginHost = nsPluginHost::GetInst();
+ nsCString permString;
+ nsresult rv = pluginHost->GetPermissionStringForTag(pluginTag, 0, permString);
+ if (rv == NS_OK) {
+ nsIPrincipal* principal = mWindow->GetExtantDoc()->NodePrincipal();
+ nsCOMPtr<nsIPermissionManager> permMgr = services::GetPermissionManager();
+ permMgr->TestPermissionFromPrincipal(principal, permString.get(), &permission);
+ }
+ }
+ }
+ if (permission == nsIPermissionManager::ALLOW_ACTION) {
+ mPlugins.AppendElement(new nsPluginElement(mWindow, pluginTags[i]));
+ } else {
+ mCTPPlugins.AppendElement(new nsPluginElement(mWindow, pluginTags[i]));
+ }
+ }
+ }
+
+ if (mPlugins.Length() == 0 && mCTPPlugins.Length() != 0) {
+ nsCOMPtr<nsPluginTag> hiddenTag = new nsPluginTag("Hidden Plugin", NULL, "dummy.plugin", NULL, NULL,
+ NULL, NULL, NULL, 0, 0, false);
+ mPlugins.AppendElement(new nsPluginElement(mWindow, hiddenTag));
+ }
+
+ // Alphabetize the enumeration order of non-hidden plugins to reduce
+ // fingerprintable entropy based on plugins' installation file times.
+ mPlugins.Sort();
+}
+
+// nsPluginElement implementation.
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(nsPluginElement)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(nsPluginElement)
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsPluginElement)
+ NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
+ NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
+
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(nsPluginElement, mWindow, mMimeTypes)
+
+nsPluginElement::nsPluginElement(nsPIDOMWindowInner* aWindow,
+ nsIInternalPluginTag* aPluginTag)
+ : mWindow(aWindow),
+ mPluginTag(aPluginTag)
+{
+}
+
+nsPluginElement::~nsPluginElement()
+{
+}
+
+nsPIDOMWindowInner*
+nsPluginElement::GetParentObject() const
+{
+ MOZ_ASSERT(mWindow);
+ return mWindow;
+}
+
+JSObject*
+nsPluginElement::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
+{
+ return PluginBinding::Wrap(aCx, this, aGivenProto);
+}
+
+void
+nsPluginElement::GetDescription(nsString& retval) const
+{
+ CopyUTF8toUTF16(mPluginTag->Description(), retval);
+}
+
+void
+nsPluginElement::GetFilename(nsString& retval) const
+{
+ CopyUTF8toUTF16(mPluginTag->FileName(), retval);
+}
+
+void
+nsPluginElement::GetVersion(nsString& retval) const
+{
+ CopyUTF8toUTF16(mPluginTag->Version(), retval);
+}
+
+void
+nsPluginElement::GetName(nsString& retval) const
+{
+ CopyUTF8toUTF16(mPluginTag->Name(), retval);
+}
+
+nsMimeType*
+nsPluginElement::Item(uint32_t aIndex)
+{
+ EnsurePluginMimeTypes();
+
+ return mMimeTypes.SafeElementAt(aIndex);
+}
+
+nsMimeType*
+nsPluginElement::NamedItem(const nsAString& aName)
+{
+ bool unused;
+ return NamedGetter(aName, unused);
+}
+
+nsMimeType*
+nsPluginElement::IndexedGetter(uint32_t aIndex, bool &aFound)
+{
+ EnsurePluginMimeTypes();
+
+ aFound = aIndex < mMimeTypes.Length();
+
+ if (!aFound) {
+ return nullptr;
+ }
+
+ return mMimeTypes[aIndex];
+}
+
+nsMimeType*
+nsPluginElement::NamedGetter(const nsAString& aName, bool &aFound)
+{
+ EnsurePluginMimeTypes();
+
+ aFound = false;
+
+ for (uint32_t i = 0; i < mMimeTypes.Length(); ++i) {
+ if (mMimeTypes[i]->Type().Equals(aName)) {
+ aFound = true;
+
+ return mMimeTypes[i];
+ }
+ }
+
+ return nullptr;
+}
+
+uint32_t
+nsPluginElement::Length()
+{
+ EnsurePluginMimeTypes();
+
+ return mMimeTypes.Length();
+}
+
+void
+nsPluginElement::GetSupportedNames(nsTArray<nsString>& retval)
+{
+ EnsurePluginMimeTypes();
+
+ for (uint32_t i = 0; i < mMimeTypes.Length(); ++i) {
+ retval.AppendElement(mMimeTypes[i]->Type());
+ }
+}
+
+nsTArray<RefPtr<nsMimeType> >&
+nsPluginElement::MimeTypes()
+{
+ EnsurePluginMimeTypes();
+
+ return mMimeTypes;
+}
+
+void
+nsPluginElement::EnsurePluginMimeTypes()
+{
+ if (!mMimeTypes.IsEmpty()) {
+ return;
+ }
+
+ if (mPluginTag->MimeTypes().Length() != mPluginTag->MimeDescriptions().Length() ||
+ mPluginTag->MimeTypes().Length() != mPluginTag->Extensions().Length()) {
+ MOZ_ASSERT(false, "mime type arrays expected to be the same length");
+ return;
+ }
+
+ for (uint32_t i = 0; i < mPluginTag->MimeTypes().Length(); ++i) {
+ NS_ConvertUTF8toUTF16 type(mPluginTag->MimeTypes()[i]);
+ NS_ConvertUTF8toUTF16 description(mPluginTag->MimeDescriptions()[i]);
+ NS_ConvertUTF8toUTF16 extension(mPluginTag->Extensions()[i]);
+
+ mMimeTypes.AppendElement(new nsMimeType(mWindow, this, type, description,
+ extension));
+ }
+}