src/3rdparty/webkit/WebCore/plugins/PluginDatabase.cpp
changeset 0 1918ee327afb
child 3 41300fa6a67c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/3rdparty/webkit/WebCore/plugins/PluginDatabase.cpp	Mon Jan 11 14:00:40 2010 +0000
@@ -0,0 +1,433 @@
+/*
+ * 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 <stdlib.h>
+
+namespace WebCore {
+
+typedef HashMap<String, RefPtr<PluginPackage> > 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<String> paths;
+    getPluginPathsInDirectories(paths);
+
+    HashMap<String, time_t> 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<String>::const_iterator pathsEnd = paths.end();
+    for (HashSet<String>::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<PluginPackage> oldPackage = m_pluginsByPath.get(*it)) {
+            ASSERT(!shouldSkipUnchangedFiles || oldPackage->lastModified() != lastModified);
+            remove(oldPackage.get());
+        }
+
+        if (!m_client || m_client->shouldLoadPluginAtPath(*it)) {
+            RefPtr<PluginPackage> 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<PluginPackage*> PluginDatabase::plugins() const
+{
+    Vector<PluginPackage*> 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<PluginPackage* const*>(a);
+    PluginPackage* pluginB = *static_cast<PluginPackage* const*>(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<PluginPackage*, 2> 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<PluginPackage*, 2> pluginChoices;
+    HashMap<PluginPackage*, String> 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<String>& 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<PluginPackage> prpPackage)
+{
+    ASSERT_ARG(prpPackage, prpPackage);
+
+    RefPtr<PluginPackage> 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(SYMBIAN)) && (!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<String> PluginDatabase::defaultPluginDirectories()
+{
+    Vector<String> 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<String> 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<String> qtPaths;
+    String qtPath(getenv("QTWEBKIT_PLUGIN_PATH"));
+    qtPath.split(UChar(':'), /* allowEmptyEntries */ false, qtPaths);
+    paths.append(qtPaths);
+#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<String>& paths) const
+{
+    // FIXME: This should be a case insensitive set.
+    HashSet<String> uniqueFilenames;
+
+#if defined(XP_UNIX)
+    String fileNameFilter("*.so");
+#else
+    String fileNameFilter("");
+#endif
+
+    Vector<String>::const_iterator dirsEnd = m_pluginDirectories.end();
+    for (Vector<String>::const_iterator dIt = m_pluginDirectories.begin(); dIt != dirsEnd; ++dIt) {
+        Vector<String> pluginPaths = listDirectory(*dIt, fileNameFilter);
+        Vector<String>::const_iterator pluginsEnd = pluginPaths.end();
+        for (Vector<String>::const_iterator pIt = pluginPaths.begin(); pIt != pluginsEnd; ++pIt) {
+            if (!fileExists(*pIt))
+                continue;
+
+            paths.add(*pIt);
+        }
+    }
+}
+
+#endif // !PLATFORM(SYMBIAN) && !PLATFORM(WIN_OS)
+
+}