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: #include "nsPluginArray.h" michael@0: michael@0: #include "mozilla/Preferences.h" michael@0: #include "mozilla/dom/PluginArrayBinding.h" michael@0: #include "mozilla/dom/PluginBinding.h" michael@0: michael@0: #include "nsCharSeparatedTokenizer.h" michael@0: #include "nsMimeTypeArray.h" michael@0: #include "Navigator.h" michael@0: #include "nsIDocShell.h" michael@0: #include "nsIWebNavigation.h" michael@0: #include "nsPluginHost.h" michael@0: #include "nsPluginTags.h" michael@0: #include "nsIObserverService.h" michael@0: #include "nsIWeakReference.h" michael@0: #include "mozilla/Services.h" michael@0: #include "nsIInterfaceRequestorUtils.h" michael@0: michael@0: using namespace mozilla; michael@0: using namespace mozilla::dom; michael@0: michael@0: nsPluginArray::nsPluginArray(nsPIDOMWindow* aWindow) michael@0: : mWindow(aWindow) michael@0: { michael@0: SetIsDOMBinding(); michael@0: } michael@0: michael@0: void michael@0: nsPluginArray::Init() michael@0: { michael@0: nsCOMPtr obsService = michael@0: mozilla::services::GetObserverService(); michael@0: if (obsService) { michael@0: obsService->AddObserver(this, "plugin-info-updated", true); michael@0: } michael@0: } michael@0: michael@0: nsPluginArray::~nsPluginArray() michael@0: { michael@0: } michael@0: michael@0: nsPIDOMWindow* michael@0: nsPluginArray::GetParentObject() const michael@0: { michael@0: MOZ_ASSERT(mWindow); michael@0: return mWindow; michael@0: } michael@0: michael@0: JSObject* michael@0: nsPluginArray::WrapObject(JSContext* aCx) michael@0: { michael@0: return PluginArrayBinding::Wrap(aCx, this); michael@0: } michael@0: michael@0: NS_IMPL_CYCLE_COLLECTING_ADDREF(nsPluginArray) michael@0: NS_IMPL_CYCLE_COLLECTING_RELEASE(nsPluginArray) michael@0: NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsPluginArray) michael@0: NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY michael@0: NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIObserver) michael@0: NS_INTERFACE_MAP_ENTRY(nsIObserver) michael@0: NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference) michael@0: NS_INTERFACE_MAP_END michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_3(nsPluginArray, michael@0: mWindow, michael@0: mPlugins, michael@0: mHiddenPlugins) michael@0: michael@0: static void michael@0: GetPluginMimeTypes(const nsTArray >& aPlugins, michael@0: nsTArray >& aMimeTypes) michael@0: { michael@0: for (uint32_t i = 0; i < aPlugins.Length(); ++i) { michael@0: nsPluginElement *plugin = aPlugins[i]; michael@0: aMimeTypes.AppendElements(plugin->MimeTypes()); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsPluginArray::GetMimeTypes(nsTArray >& aMimeTypes, michael@0: nsTArray >& aHiddenMimeTypes) michael@0: { michael@0: aMimeTypes.Clear(); michael@0: aHiddenMimeTypes.Clear(); michael@0: michael@0: if (!AllowPlugins()) { michael@0: return; michael@0: } michael@0: michael@0: EnsurePlugins(); michael@0: michael@0: GetPluginMimeTypes(mPlugins, aMimeTypes); michael@0: GetPluginMimeTypes(mHiddenPlugins, aHiddenMimeTypes); michael@0: } michael@0: michael@0: nsPluginElement* michael@0: nsPluginArray::Item(uint32_t aIndex) michael@0: { michael@0: bool unused; michael@0: return IndexedGetter(aIndex, unused); michael@0: } michael@0: michael@0: nsPluginElement* michael@0: nsPluginArray::NamedItem(const nsAString& aName) michael@0: { michael@0: bool unused; michael@0: return NamedGetter(aName, unused); michael@0: } michael@0: michael@0: void michael@0: nsPluginArray::Refresh(bool aReloadDocuments) michael@0: { michael@0: nsRefPtr pluginHost = nsPluginHost::GetInst(); michael@0: michael@0: if(!AllowPlugins() || !pluginHost) { michael@0: return; michael@0: } michael@0: michael@0: // NS_ERROR_PLUGINS_PLUGINSNOTCHANGED on reloading plugins indicates michael@0: // that plugins did not change and was not reloaded michael@0: if (pluginHost->ReloadPlugins() == michael@0: NS_ERROR_PLUGINS_PLUGINSNOTCHANGED) { michael@0: nsTArray > newPluginTags; michael@0: pluginHost->GetPlugins(newPluginTags); michael@0: michael@0: // Check if the number of plugins we know about are different from michael@0: // the number of plugin tags the plugin host knows about. If the michael@0: // lengths are different, we refresh. This is safe because we're michael@0: // notified for every plugin enabling/disabling event that michael@0: // happens, and therefore the lengths will be in sync only when michael@0: // the both arrays contain the same plugin tags (though as michael@0: // different types). michael@0: uint32_t pluginCount = mPlugins.Length() + mHiddenPlugins.Length(); michael@0: if (newPluginTags.Length() == pluginCount) { michael@0: return; michael@0: } michael@0: } michael@0: michael@0: mPlugins.Clear(); michael@0: mHiddenPlugins.Clear(); michael@0: michael@0: nsCOMPtr navigator; michael@0: mWindow->GetNavigator(getter_AddRefs(navigator)); michael@0: michael@0: if (!navigator) { michael@0: return; michael@0: } michael@0: michael@0: static_cast(navigator.get())->RefreshMIMEArray(); michael@0: michael@0: nsCOMPtr webNav = do_GetInterface(mWindow); michael@0: if (aReloadDocuments && webNav) { michael@0: webNav->Reload(nsIWebNavigation::LOAD_FLAGS_NONE); michael@0: } michael@0: } michael@0: michael@0: nsPluginElement* michael@0: nsPluginArray::IndexedGetter(uint32_t aIndex, bool &aFound) michael@0: { michael@0: aFound = false; michael@0: michael@0: if (!AllowPlugins()) { michael@0: return nullptr; michael@0: } michael@0: michael@0: EnsurePlugins(); michael@0: michael@0: aFound = aIndex < mPlugins.Length(); michael@0: michael@0: return aFound ? mPlugins[aIndex] : nullptr; michael@0: } michael@0: michael@0: void michael@0: nsPluginArray::Invalidate() michael@0: { michael@0: nsCOMPtr obsService = michael@0: mozilla::services::GetObserverService(); michael@0: if (obsService) { michael@0: obsService->RemoveObserver(this, "plugin-info-updated"); michael@0: } michael@0: } michael@0: michael@0: static nsPluginElement* michael@0: FindPlugin(const nsTArray >& aPlugins, michael@0: const nsAString& aName) michael@0: { michael@0: for (uint32_t i = 0; i < aPlugins.Length(); ++i) { michael@0: nsAutoString pluginName; michael@0: nsPluginElement* plugin = aPlugins[i]; michael@0: plugin->GetName(pluginName); michael@0: michael@0: if (pluginName.Equals(aName)) { michael@0: return plugin; michael@0: } michael@0: } michael@0: michael@0: return nullptr; michael@0: } michael@0: michael@0: nsPluginElement* michael@0: nsPluginArray::NamedGetter(const nsAString& aName, bool &aFound) michael@0: { michael@0: aFound = false; michael@0: michael@0: if (!AllowPlugins()) { michael@0: return nullptr; michael@0: } michael@0: michael@0: EnsurePlugins(); michael@0: michael@0: nsPluginElement* plugin = FindPlugin(mPlugins, aName); michael@0: if (!plugin) { michael@0: plugin = FindPlugin(mHiddenPlugins, aName); michael@0: } michael@0: michael@0: aFound = (plugin != nullptr); michael@0: return plugin; michael@0: } michael@0: michael@0: bool michael@0: nsPluginArray::NameIsEnumerable(const nsAString& aName) michael@0: { michael@0: return true; michael@0: } michael@0: michael@0: uint32_t michael@0: nsPluginArray::Length() michael@0: { michael@0: if (!AllowPlugins()) { michael@0: return 0; michael@0: } michael@0: michael@0: EnsurePlugins(); michael@0: michael@0: return mPlugins.Length(); michael@0: } michael@0: michael@0: void michael@0: nsPluginArray::GetSupportedNames(unsigned, nsTArray& aRetval) michael@0: { michael@0: aRetval.Clear(); michael@0: michael@0: if (!AllowPlugins()) { michael@0: return; michael@0: } michael@0: michael@0: for (uint32_t i = 0; i < mPlugins.Length(); ++i) { michael@0: nsAutoString pluginName; michael@0: mPlugins[i]->GetName(pluginName); michael@0: michael@0: aRetval.AppendElement(pluginName); michael@0: } michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsPluginArray::Observe(nsISupports *aSubject, const char *aTopic, michael@0: const char16_t *aData) { michael@0: if (!nsCRT::strcmp(aTopic, "plugin-info-updated")) { michael@0: Refresh(false); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: bool michael@0: nsPluginArray::AllowPlugins() const michael@0: { michael@0: nsCOMPtr docShell = do_GetInterface(mWindow); michael@0: michael@0: return docShell && docShell->PluginsAllowedInCurrentDoc(); michael@0: } michael@0: michael@0: static bool michael@0: HasStringPrefix(const nsCString& str, const nsACString& prefix) { michael@0: return str.Compare(prefix.BeginReading(), false, prefix.Length()) == 0; michael@0: } michael@0: michael@0: static bool michael@0: IsPluginEnumerable(const nsTArray& enumerableNames, michael@0: const nsPluginTag* pluginTag) michael@0: { michael@0: const nsCString& pluginName = pluginTag->mName; michael@0: michael@0: const uint32_t length = enumerableNames.Length(); michael@0: for (uint32_t i = 0; i < length; i++) { michael@0: const nsCString& name = enumerableNames[i]; michael@0: if (HasStringPrefix(pluginName, name)) { michael@0: return true; // don't hide plugin michael@0: } michael@0: } michael@0: michael@0: return false; // hide plugin! michael@0: } michael@0: michael@0: void michael@0: nsPluginArray::EnsurePlugins() michael@0: { michael@0: if (!mPlugins.IsEmpty() || !mHiddenPlugins.IsEmpty()) { michael@0: // We already have an array of plugin elements. michael@0: return; michael@0: } michael@0: michael@0: nsRefPtr pluginHost = nsPluginHost::GetInst(); michael@0: if (!pluginHost) { michael@0: // We have no plugin host. michael@0: return; michael@0: } michael@0: michael@0: nsTArray > pluginTags; michael@0: pluginHost->GetPlugins(pluginTags); michael@0: michael@0: nsTArray enumerableNames; michael@0: michael@0: const nsAdoptingCString& enumerableNamesPref = michael@0: Preferences::GetCString("plugins.enumerable_names"); michael@0: michael@0: bool disablePluginHiding = !enumerableNamesPref || michael@0: enumerableNamesPref.EqualsLiteral("*"); michael@0: michael@0: if (!disablePluginHiding) { michael@0: nsCCharSeparatedTokenizer tokens(enumerableNamesPref, ','); michael@0: while (tokens.hasMoreTokens()) { michael@0: const nsCSubstring& token = tokens.nextToken(); michael@0: if (!token.IsEmpty()) { michael@0: enumerableNames.AppendElement(token); michael@0: } michael@0: } michael@0: } michael@0: michael@0: // need to wrap each of these with a nsPluginElement, which is michael@0: // scriptable. michael@0: for (uint32_t i = 0; i < pluginTags.Length(); ++i) { michael@0: nsPluginTag* pluginTag = pluginTags[i]; michael@0: michael@0: // Add the plugin to the list of hidden plugins or non-hidden plugins? michael@0: nsTArray >& pluginArray = michael@0: (disablePluginHiding || IsPluginEnumerable(enumerableNames, pluginTag)) michael@0: ? mPlugins michael@0: : mHiddenPlugins; michael@0: michael@0: pluginArray.AppendElement(new nsPluginElement(mWindow, pluginTag)); michael@0: } michael@0: } michael@0: michael@0: // nsPluginElement implementation. michael@0: michael@0: NS_IMPL_CYCLE_COLLECTING_ADDREF(nsPluginElement) michael@0: NS_IMPL_CYCLE_COLLECTING_RELEASE(nsPluginElement) michael@0: NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsPluginElement) michael@0: NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY michael@0: NS_INTERFACE_MAP_ENTRY(nsISupports) michael@0: NS_INTERFACE_MAP_END michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_2(nsPluginElement, mWindow, mMimeTypes) michael@0: michael@0: nsPluginElement::nsPluginElement(nsPIDOMWindow* aWindow, michael@0: nsPluginTag* aPluginTag) michael@0: : mWindow(aWindow), michael@0: mPluginTag(aPluginTag) michael@0: { michael@0: SetIsDOMBinding(); michael@0: } michael@0: michael@0: nsPIDOMWindow* michael@0: nsPluginElement::GetParentObject() const michael@0: { michael@0: MOZ_ASSERT(mWindow); michael@0: return mWindow; michael@0: } michael@0: michael@0: JSObject* michael@0: nsPluginElement::WrapObject(JSContext* aCx) michael@0: { michael@0: return PluginBinding::Wrap(aCx, this); michael@0: } michael@0: michael@0: void michael@0: nsPluginElement::GetDescription(nsString& retval) const michael@0: { michael@0: CopyUTF8toUTF16(mPluginTag->mDescription, retval); michael@0: } michael@0: michael@0: void michael@0: nsPluginElement::GetFilename(nsString& retval) const michael@0: { michael@0: CopyUTF8toUTF16(mPluginTag->mFileName, retval); michael@0: } michael@0: michael@0: void michael@0: nsPluginElement::GetVersion(nsString& retval) const michael@0: { michael@0: CopyUTF8toUTF16(mPluginTag->mVersion, retval); michael@0: } michael@0: michael@0: void michael@0: nsPluginElement::GetName(nsString& retval) const michael@0: { michael@0: CopyUTF8toUTF16(mPluginTag->mName, retval); michael@0: } michael@0: michael@0: nsMimeType* michael@0: nsPluginElement::Item(uint32_t aIndex) michael@0: { michael@0: EnsurePluginMimeTypes(); michael@0: michael@0: return mMimeTypes.SafeElementAt(aIndex); michael@0: } michael@0: michael@0: nsMimeType* michael@0: nsPluginElement::NamedItem(const nsAString& aName) michael@0: { michael@0: bool unused; michael@0: return NamedGetter(aName, unused); michael@0: } michael@0: michael@0: nsMimeType* michael@0: nsPluginElement::IndexedGetter(uint32_t aIndex, bool &aFound) michael@0: { michael@0: EnsurePluginMimeTypes(); michael@0: michael@0: aFound = aIndex < mMimeTypes.Length(); michael@0: michael@0: return aFound ? mMimeTypes[aIndex] : nullptr; michael@0: } michael@0: michael@0: nsMimeType* michael@0: nsPluginElement::NamedGetter(const nsAString& aName, bool &aFound) michael@0: { michael@0: EnsurePluginMimeTypes(); michael@0: michael@0: aFound = false; michael@0: michael@0: for (uint32_t i = 0; i < mMimeTypes.Length(); ++i) { michael@0: if (mMimeTypes[i]->Type().Equals(aName)) { michael@0: aFound = true; michael@0: michael@0: return mMimeTypes[i]; michael@0: } michael@0: } michael@0: michael@0: return nullptr; michael@0: } michael@0: michael@0: bool michael@0: nsPluginElement::NameIsEnumerable(const nsAString& aName) michael@0: { michael@0: return true; michael@0: } michael@0: michael@0: uint32_t michael@0: nsPluginElement::Length() michael@0: { michael@0: EnsurePluginMimeTypes(); michael@0: michael@0: return mMimeTypes.Length(); michael@0: } michael@0: michael@0: void michael@0: nsPluginElement::GetSupportedNames(unsigned, nsTArray& retval) michael@0: { michael@0: EnsurePluginMimeTypes(); michael@0: michael@0: for (uint32_t i = 0; i < mMimeTypes.Length(); ++i) { michael@0: retval.AppendElement(mMimeTypes[i]->Type()); michael@0: } michael@0: } michael@0: michael@0: nsTArray >& michael@0: nsPluginElement::MimeTypes() michael@0: { michael@0: EnsurePluginMimeTypes(); michael@0: michael@0: return mMimeTypes; michael@0: } michael@0: michael@0: void michael@0: nsPluginElement::EnsurePluginMimeTypes() michael@0: { michael@0: if (!mMimeTypes.IsEmpty()) { michael@0: return; michael@0: } michael@0: michael@0: for (uint32_t i = 0; i < mPluginTag->mMimeTypes.Length(); ++i) { michael@0: NS_ConvertUTF8toUTF16 type(mPluginTag->mMimeTypes[i]); michael@0: mMimeTypes.AppendElement(new nsMimeType(mWindow, this, i, type)); michael@0: } michael@0: }