/* * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. * Copyright (C) 2008 Collabora, Ltd. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "config.h" #include "PluginDatabase.h" #include "Frame.h" #include "KURL.h" #include "PluginDatabaseClient.h" #include "PluginPackage.h" #include #if PLATFORM(ANDROID) #include "JavaSharedClient.h" #include "PluginClient.h" #endif namespace WebCore { typedef HashMap > PluginPackageByNameMap; PluginDatabase::PluginDatabase() : m_client(0) { } PluginDatabase* PluginDatabase::installedPlugins(bool populate) { static PluginDatabase* plugins = 0; if (!plugins) { plugins = new PluginDatabase; if (populate) { plugins->setPluginDirectories(PluginDatabase::defaultPluginDirectories()); plugins->refresh(); } } return plugins; } bool PluginDatabase::isMIMETypeRegistered(const String& mimeType) { if (mimeType.isNull()) return false; if (m_registeredMIMETypes.contains(mimeType)) return true; // No plugin was found, try refreshing the database and searching again return (refresh() && m_registeredMIMETypes.contains(mimeType)); } void PluginDatabase::addExtraPluginDirectory(const String& directory) { m_pluginDirectories.append(directory); refresh(); } bool PluginDatabase::refresh() { bool pluginSetChanged = false; if (!m_plugins.isEmpty()) { PluginSet pluginsToUnload; getDeletedPlugins(pluginsToUnload); // Unload plugins PluginSet::const_iterator end = pluginsToUnload.end(); for (PluginSet::const_iterator it = pluginsToUnload.begin(); it != end; ++it) remove(it->get()); pluginSetChanged = !pluginsToUnload.isEmpty(); } HashSet paths; getPluginPathsInDirectories(paths); HashMap pathsWithTimes; // We should only skip unchanged files if we didn't remove any plugins above. If we did remove // any plugins, we need to look at every plugin file so that, e.g., if the user has two versions // of RealPlayer installed and just removed the newer one, we'll pick up the older one. bool shouldSkipUnchangedFiles = !pluginSetChanged; HashSet::const_iterator pathsEnd = paths.end(); for (HashSet::const_iterator it = paths.begin(); it != pathsEnd; ++it) { time_t lastModified; if (!getFileModificationTime(*it, lastModified)) continue; pathsWithTimes.add(*it, lastModified); // If the path's timestamp hasn't changed since the last time we ran refresh(), we don't have to do anything. if (shouldSkipUnchangedFiles && m_pluginPathsWithTimes.get(*it) == lastModified) continue; if (RefPtr oldPackage = m_pluginsByPath.get(*it)) { ASSERT(!shouldSkipUnchangedFiles || oldPackage->lastModified() != lastModified); remove(oldPackage.get()); } if (!m_client || m_client->shouldLoadPluginAtPath(*it)) { RefPtr package = PluginPackage::createPackage(*it, lastModified); if (package && (!m_client || m_client->shouldLoadPluginPackage(package.get())) && add(package.release())) pluginSetChanged = true; } } // Cache all the paths we found with their timestamps for next time. pathsWithTimes.swap(m_pluginPathsWithTimes); if (!pluginSetChanged) return false; m_registeredMIMETypes.clear(); // Register plug-in MIME types PluginSet::const_iterator end = m_plugins.end(); for (PluginSet::const_iterator it = m_plugins.begin(); it != end; ++it) { // Get MIME types MIMEToDescriptionsMap::const_iterator map_it = (*it)->mimeToDescriptions().begin(); MIMEToDescriptionsMap::const_iterator map_end = (*it)->mimeToDescriptions().end(); for (; map_it != map_end; ++map_it) m_registeredMIMETypes.add(map_it->first); } return true; } Vector PluginDatabase::plugins() const { Vector result; PluginSet::const_iterator end = m_plugins.end(); for (PluginSet::const_iterator it = m_plugins.begin(); it != end; ++it) result.append((*it).get()); return result; } int PluginDatabase::preferredPluginCompare(const void* a, const void* b) { PluginPackage* pluginA = *static_cast(a); PluginPackage* pluginB = *static_cast(b); return pluginA->compare(*pluginB); } PluginPackage* PluginDatabase::pluginForMIMEType(const String& mimeType) { if (mimeType.isEmpty()) return 0; String key = mimeType.lower(); PluginSet::const_iterator end = m_plugins.end(); PluginPackage* preferredPlugin = m_preferredPlugins.get(key).get(); if (preferredPlugin && preferredPlugin->isEnabled() && preferredPlugin->mimeToDescriptions().contains(key)) { return preferredPlugin; } Vector pluginChoices; for (PluginSet::const_iterator it = m_plugins.begin(); it != end; ++it) { PluginPackage* plugin = (*it).get(); if (!plugin->isEnabled()) continue; if (plugin->mimeToDescriptions().contains(key)) pluginChoices.append(plugin); } if (pluginChoices.isEmpty()) return 0; qsort(pluginChoices.data(), pluginChoices.size(), sizeof(PluginPackage*), PluginDatabase::preferredPluginCompare); return pluginChoices[0]; } String PluginDatabase::MIMETypeForExtension(const String& extension) const { if (extension.isEmpty()) return String(); PluginSet::const_iterator end = m_plugins.end(); String mimeType; Vector pluginChoices; HashMap mimeTypeForPlugin; for (PluginSet::const_iterator it = m_plugins.begin(); it != end; ++it) { if (!(*it)->isEnabled()) continue; MIMEToExtensionsMap::const_iterator mime_end = (*it)->mimeToExtensions().end(); for (MIMEToExtensionsMap::const_iterator mime_it = (*it)->mimeToExtensions().begin(); mime_it != mime_end; ++mime_it) { mimeType = mime_it->first; PluginPackage* preferredPlugin = m_preferredPlugins.get(mimeType).get(); const Vector& extensions = mime_it->second; bool foundMapping = false; for (unsigned i = 0; i < extensions.size(); i++) { if (equalIgnoringCase(extensions[i], extension)) { PluginPackage* plugin = (*it).get(); if (preferredPlugin && PluginPackage::equal(*plugin, *preferredPlugin)) return mimeType; pluginChoices.append(plugin); mimeTypeForPlugin.add(plugin, mimeType); foundMapping = true; break; } } if (foundMapping) break; } } if (pluginChoices.isEmpty()) return String(); qsort(pluginChoices.data(), pluginChoices.size(), sizeof(PluginPackage*), PluginDatabase::preferredPluginCompare); return mimeTypeForPlugin.get(pluginChoices[0]); } PluginPackage* PluginDatabase::findPlugin(const KURL& url, String& mimeType) { PluginPackage* plugin = pluginForMIMEType(mimeType); String filename = url.string(); if (!plugin) { String filename = url.lastPathComponent(); if (!filename.endsWith("/")) { int extensionPos = filename.reverseFind('.'); if (extensionPos != -1) { String extension = filename.substring(extensionPos + 1); mimeType = MIMETypeForExtension(extension); plugin = pluginForMIMEType(mimeType); } } } // FIXME: if no plugin could be found, query Windows for the mime type // corresponding to the extension. return plugin; } void PluginDatabase::setPreferredPluginForMIMEType(const String& mimeType, PluginPackage* plugin) { if (!plugin || plugin->mimeToExtensions().contains(mimeType)) m_preferredPlugins.set(mimeType.lower(), plugin); } void PluginDatabase::getDeletedPlugins(PluginSet& plugins) const { PluginSet::const_iterator end = m_plugins.end(); for (PluginSet::const_iterator it = m_plugins.begin(); it != end; ++it) { if (!fileExists((*it)->path())) plugins.add(*it); } } bool PluginDatabase::add(PassRefPtr prpPackage) { ASSERT_ARG(prpPackage, prpPackage); RefPtr package = prpPackage; if (!m_plugins.add(package).second) return false; m_pluginsByPath.add(package->path(), package); return true; } void PluginDatabase::remove(PluginPackage* package) { MIMEToExtensionsMap::const_iterator it = package->mimeToExtensions().begin(); MIMEToExtensionsMap::const_iterator end = package->mimeToExtensions().end(); for ( ; it != end; ++it) { PluginPackageByNameMap::iterator packageInMap = m_preferredPlugins.find(it->first); if (packageInMap != m_preferredPlugins.end() && packageInMap->second == package) m_preferredPlugins.remove(packageInMap); } m_plugins.remove(package); m_pluginsByPath.remove(package->path()); } void PluginDatabase::clear() { m_plugins.clear(); m_pluginsByPath.clear(); m_pluginPathsWithTimes.clear(); m_registeredMIMETypes.clear(); m_preferredPlugins.clear(); } #if !PLATFORM(WIN_OS) || PLATFORM(WX) // For Safari/Win the following three methods are implemented // in PluginDatabaseWin.cpp, but if we can use WebCore constructs // for the logic we should perhaps move it here under XP_WIN? Vector PluginDatabase::defaultPluginDirectories() { Vector paths; // Add paths specific to each platform #if defined(XP_UNIX) String userPluginPath = homeDirectoryPath(); userPluginPath.append(String("/.mozilla/plugins")); paths.append(userPluginPath); userPluginPath = homeDirectoryPath(); userPluginPath.append(String("/.netscape/plugins")); paths.append(userPluginPath); paths.append("/usr/lib/browser/plugins"); paths.append("/usr/local/lib/mozilla/plugins"); paths.append("/usr/lib/firefox/plugins"); paths.append("/usr/lib64/browser-plugins"); paths.append("/usr/lib/browser-plugins"); paths.append("/usr/lib/mozilla/plugins"); paths.append("/usr/local/netscape/plugins"); paths.append("/opt/mozilla/plugins"); paths.append("/opt/mozilla/lib/plugins"); paths.append("/opt/netscape/plugins"); paths.append("/opt/netscape/communicator/plugins"); paths.append("/usr/lib/netscape/plugins"); paths.append("/usr/lib/netscape/plugins-libc5"); paths.append("/usr/lib/netscape/plugins-libc6"); paths.append("/usr/lib64/netscape/plugins"); paths.append("/usr/lib64/mozilla/plugins"); paths.append("/usr/lib/nsbrowser/plugins"); paths.append("/usr/lib64/nsbrowser/plugins"); String mozHome(getenv("MOZILLA_HOME")); mozHome.append("/plugins"); paths.append(mozHome); Vector mozPaths; String mozPath(getenv("MOZ_PLUGIN_PATH")); mozPath.split(UChar(':'), /* allowEmptyEntries */ false, mozPaths); paths.append(mozPaths); #elif defined(XP_MACOSX) String userPluginPath = homeDirectoryPath(); userPluginPath.append(String("/Library/Internet Plug-Ins")); paths.append(userPluginPath); paths.append("/Library/Internet Plug-Ins"); #elif defined(XP_WIN) String userPluginPath = homeDirectoryPath(); userPluginPath.append(String("\\Application Data\\Mozilla\\plugins")); paths.append(userPluginPath); #endif // Add paths specific to each port #if PLATFORM(QT) Vector qtPaths; String qtPath(getenv("QTWEBKIT_PLUGIN_PATH")); qtPath.split(UChar(':'), /* allowEmptyEntries */ false, qtPaths); paths.append(qtPaths); #endif #if PLATFORM(ANDROID) if (android::JavaSharedClient::GetPluginClient()) return android::JavaSharedClient::GetPluginClient()->getPluginDirectories(); #endif return paths; } bool PluginDatabase::isPreferredPluginDirectory(const String& path) { String preferredPath = homeDirectoryPath(); #if defined(XP_UNIX) preferredPath.append(String("/.mozilla/plugins")); #elif defined(XP_MACOSX) preferredPath.append(String("/Library/Internet Plug-Ins")); #elif defined(XP_WIN) preferredPath.append(String("\\Application Data\\Mozilla\\plugins")); #endif // TODO: We should normalize the path before doing a comparison. return path == preferredPath; } void PluginDatabase::getPluginPathsInDirectories(HashSet& paths) const { // FIXME: This should be a case insensitive set. HashSet uniqueFilenames; #if defined(XP_UNIX) || defined(ANDROID) String fileNameFilter("*.so"); #else String fileNameFilter(""); #endif Vector::const_iterator dirsEnd = m_pluginDirectories.end(); for (Vector::const_iterator dIt = m_pluginDirectories.begin(); dIt != dirsEnd; ++dIt) { Vector pluginPaths = listDirectory(*dIt, fileNameFilter); Vector::const_iterator pluginsEnd = pluginPaths.end(); for (Vector::const_iterator pIt = pluginPaths.begin(); pIt != pluginsEnd; ++pIt) { if (!fileExists(*pIt)) continue; paths.add(*pIt); } } } #endif // !PLATFORM(WIN_OS) }