src/3rdparty/webkit/WebCore/plugins/PluginDatabase.cpp
changeset 0 1918ee327afb
child 3 41300fa6a67c
equal deleted inserted replaced
-1:000000000000 0:1918ee327afb
       
     1 /*
       
     2  * Copyright (C) 2006, 2007 Apple Inc.  All rights reserved.
       
     3  * Copyright (C) 2008 Collabora, Ltd.  All rights reserved.
       
     4  *
       
     5  * Redistribution and use in source and binary forms, with or without
       
     6  * modification, are permitted provided that the following conditions
       
     7  * are met:
       
     8  * 1. Redistributions of source code must retain the above copyright
       
     9  *    notice, this list of conditions and the following disclaimer.
       
    10  * 2. Redistributions in binary form must reproduce the above copyright
       
    11  *    notice, this list of conditions and the following disclaimer in the
       
    12  *    documentation and/or other materials provided with the distribution.
       
    13  *
       
    14  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
       
    15  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
       
    16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
       
    17  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
       
    18  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
       
    19  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
       
    20  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
       
    21  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
       
    22  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
       
    23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
       
    24  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
       
    25  */
       
    26 
       
    27 #include "config.h"
       
    28 #include "PluginDatabase.h"
       
    29 
       
    30 #include "Frame.h"
       
    31 #include "KURL.h"
       
    32 #include "PluginDatabaseClient.h"
       
    33 #include "PluginPackage.h"
       
    34 #include <stdlib.h>
       
    35 
       
    36 namespace WebCore {
       
    37 
       
    38 typedef HashMap<String, RefPtr<PluginPackage> > PluginPackageByNameMap;
       
    39 
       
    40 PluginDatabase::PluginDatabase()
       
    41     : m_client(0)
       
    42 {
       
    43 }
       
    44 
       
    45 PluginDatabase* PluginDatabase::installedPlugins(bool populate)
       
    46 {
       
    47     static PluginDatabase* plugins = 0;
       
    48 
       
    49     if (!plugins) {
       
    50         plugins = new PluginDatabase;
       
    51 
       
    52         if (populate) {
       
    53             plugins->setPluginDirectories(PluginDatabase::defaultPluginDirectories());
       
    54             plugins->refresh();
       
    55         }
       
    56     }
       
    57 
       
    58     return plugins;
       
    59 }
       
    60 
       
    61 bool PluginDatabase::isMIMETypeRegistered(const String& mimeType)
       
    62 {
       
    63     if (mimeType.isNull())
       
    64         return false;
       
    65     if (m_registeredMIMETypes.contains(mimeType))
       
    66         return true;
       
    67     // No plugin was found, try refreshing the database and searching again
       
    68     return (refresh() && m_registeredMIMETypes.contains(mimeType));
       
    69 }
       
    70 
       
    71 void PluginDatabase::addExtraPluginDirectory(const String& directory)
       
    72 {
       
    73     m_pluginDirectories.append(directory);
       
    74     refresh();
       
    75 }
       
    76 
       
    77 bool PluginDatabase::refresh()
       
    78 {
       
    79     bool pluginSetChanged = false;
       
    80 
       
    81     if (!m_plugins.isEmpty()) {
       
    82         PluginSet pluginsToUnload;
       
    83         getDeletedPlugins(pluginsToUnload);
       
    84 
       
    85         // Unload plugins
       
    86         PluginSet::const_iterator end = pluginsToUnload.end();
       
    87         for (PluginSet::const_iterator it = pluginsToUnload.begin(); it != end; ++it)
       
    88             remove(it->get());
       
    89 
       
    90         pluginSetChanged = !pluginsToUnload.isEmpty();
       
    91     }
       
    92 
       
    93     HashSet<String> paths;
       
    94     getPluginPathsInDirectories(paths);
       
    95 
       
    96     HashMap<String, time_t> pathsWithTimes;
       
    97 
       
    98     // We should only skip unchanged files if we didn't remove any plugins above. If we did remove
       
    99     // any plugins, we need to look at every plugin file so that, e.g., if the user has two versions
       
   100     // of RealPlayer installed and just removed the newer one, we'll pick up the older one.
       
   101     bool shouldSkipUnchangedFiles = !pluginSetChanged;
       
   102 
       
   103     HashSet<String>::const_iterator pathsEnd = paths.end();
       
   104     for (HashSet<String>::const_iterator it = paths.begin(); it != pathsEnd; ++it) {
       
   105         time_t lastModified;
       
   106         if (!getFileModificationTime(*it, lastModified))
       
   107             continue;
       
   108 
       
   109         pathsWithTimes.add(*it, lastModified);
       
   110 
       
   111         // If the path's timestamp hasn't changed since the last time we ran refresh(), we don't have to do anything.
       
   112         if (shouldSkipUnchangedFiles && m_pluginPathsWithTimes.get(*it) == lastModified)
       
   113             continue;
       
   114 
       
   115         if (RefPtr<PluginPackage> oldPackage = m_pluginsByPath.get(*it)) {
       
   116             ASSERT(!shouldSkipUnchangedFiles || oldPackage->lastModified() != lastModified);
       
   117             remove(oldPackage.get());
       
   118         }
       
   119 
       
   120         if (!m_client || m_client->shouldLoadPluginAtPath(*it)) {
       
   121             RefPtr<PluginPackage> package = PluginPackage::createPackage(*it, lastModified);
       
   122             if (package && (!m_client || m_client->shouldLoadPluginPackage(package.get())) && add(package.release()))
       
   123                 pluginSetChanged = true;
       
   124         }
       
   125     }
       
   126 
       
   127     // Cache all the paths we found with their timestamps for next time.
       
   128     pathsWithTimes.swap(m_pluginPathsWithTimes);
       
   129 
       
   130     if (!pluginSetChanged)
       
   131         return false;
       
   132 
       
   133     m_registeredMIMETypes.clear();
       
   134 
       
   135     // Register plug-in MIME types
       
   136     PluginSet::const_iterator end = m_plugins.end();
       
   137     for (PluginSet::const_iterator it = m_plugins.begin(); it != end; ++it) {
       
   138         // Get MIME types
       
   139         MIMEToDescriptionsMap::const_iterator map_it = (*it)->mimeToDescriptions().begin();
       
   140         MIMEToDescriptionsMap::const_iterator map_end = (*it)->mimeToDescriptions().end();
       
   141         for (; map_it != map_end; ++map_it)
       
   142             m_registeredMIMETypes.add(map_it->first);
       
   143     }
       
   144 
       
   145     return true;
       
   146 }
       
   147 
       
   148 Vector<PluginPackage*> PluginDatabase::plugins() const
       
   149 {
       
   150     Vector<PluginPackage*> result;
       
   151 
       
   152     PluginSet::const_iterator end = m_plugins.end();
       
   153     for (PluginSet::const_iterator it = m_plugins.begin(); it != end; ++it)
       
   154         result.append((*it).get());
       
   155 
       
   156     return result;
       
   157 }
       
   158 
       
   159 int PluginDatabase::preferredPluginCompare(const void* a, const void* b)
       
   160 {
       
   161     PluginPackage* pluginA = *static_cast<PluginPackage* const*>(a);
       
   162     PluginPackage* pluginB = *static_cast<PluginPackage* const*>(b);
       
   163 
       
   164     return pluginA->compare(*pluginB);
       
   165 }
       
   166 
       
   167 PluginPackage* PluginDatabase::pluginForMIMEType(const String& mimeType)
       
   168 {
       
   169     if (mimeType.isEmpty())
       
   170         return 0;
       
   171 
       
   172     String key = mimeType.lower();
       
   173     PluginSet::const_iterator end = m_plugins.end();
       
   174     PluginPackage* preferredPlugin = m_preferredPlugins.get(key).get();
       
   175     if (preferredPlugin
       
   176         && preferredPlugin->isEnabled()
       
   177         && preferredPlugin->mimeToDescriptions().contains(key)) {
       
   178         return preferredPlugin;
       
   179     }
       
   180 
       
   181     Vector<PluginPackage*, 2> pluginChoices;
       
   182 
       
   183     for (PluginSet::const_iterator it = m_plugins.begin(); it != end; ++it) {
       
   184         PluginPackage* plugin = (*it).get();
       
   185 
       
   186         if (!plugin->isEnabled())
       
   187             continue;
       
   188 
       
   189         if (plugin->mimeToDescriptions().contains(key))
       
   190             pluginChoices.append(plugin);
       
   191     }
       
   192 
       
   193     if (pluginChoices.isEmpty())
       
   194         return 0;
       
   195 
       
   196     qsort(pluginChoices.data(), pluginChoices.size(), sizeof(PluginPackage*), PluginDatabase::preferredPluginCompare);
       
   197 
       
   198     return pluginChoices[0];
       
   199 }
       
   200 
       
   201 String PluginDatabase::MIMETypeForExtension(const String& extension) const
       
   202 {
       
   203     if (extension.isEmpty())
       
   204         return String();
       
   205 
       
   206     PluginSet::const_iterator end = m_plugins.end();
       
   207     String mimeType;
       
   208     Vector<PluginPackage*, 2> pluginChoices;
       
   209     HashMap<PluginPackage*, String> mimeTypeForPlugin;
       
   210 
       
   211     for (PluginSet::const_iterator it = m_plugins.begin(); it != end; ++it) {
       
   212         if (!(*it)->isEnabled())
       
   213             continue;
       
   214 
       
   215         MIMEToExtensionsMap::const_iterator mime_end = (*it)->mimeToExtensions().end();
       
   216 
       
   217         for (MIMEToExtensionsMap::const_iterator mime_it = (*it)->mimeToExtensions().begin(); mime_it != mime_end; ++mime_it) {
       
   218             mimeType = mime_it->first;
       
   219             PluginPackage* preferredPlugin = m_preferredPlugins.get(mimeType).get();
       
   220             const Vector<String>& extensions = mime_it->second;
       
   221             bool foundMapping = false;
       
   222             for (unsigned i = 0; i < extensions.size(); i++) {
       
   223                 if (equalIgnoringCase(extensions[i], extension)) {
       
   224                     PluginPackage* plugin = (*it).get();
       
   225 
       
   226                     if (preferredPlugin && PluginPackage::equal(*plugin, *preferredPlugin))
       
   227                         return mimeType;
       
   228 
       
   229                     pluginChoices.append(plugin);
       
   230                     mimeTypeForPlugin.add(plugin, mimeType);
       
   231                     foundMapping = true;
       
   232                     break;
       
   233                 }
       
   234             }
       
   235             if (foundMapping)
       
   236                 break;
       
   237         }
       
   238     }
       
   239 
       
   240     if (pluginChoices.isEmpty())
       
   241         return String();
       
   242 
       
   243     qsort(pluginChoices.data(), pluginChoices.size(), sizeof(PluginPackage*), PluginDatabase::preferredPluginCompare);
       
   244 
       
   245     return mimeTypeForPlugin.get(pluginChoices[0]);
       
   246 }
       
   247 
       
   248 PluginPackage* PluginDatabase::findPlugin(const KURL& url, String& mimeType)
       
   249 {
       
   250     PluginPackage* plugin = pluginForMIMEType(mimeType);
       
   251     String filename = url.string();
       
   252 
       
   253     if (!plugin) {
       
   254         String filename = url.lastPathComponent();
       
   255         if (!filename.endsWith("/")) {
       
   256             int extensionPos = filename.reverseFind('.');
       
   257             if (extensionPos != -1) {
       
   258                 String extension = filename.substring(extensionPos + 1);
       
   259 
       
   260                 mimeType = MIMETypeForExtension(extension);
       
   261                 plugin = pluginForMIMEType(mimeType);
       
   262             }
       
   263         }
       
   264     }
       
   265 
       
   266     // FIXME: if no plugin could be found, query Windows for the mime type
       
   267     // corresponding to the extension.
       
   268 
       
   269     return plugin;
       
   270 }
       
   271 
       
   272 void PluginDatabase::setPreferredPluginForMIMEType(const String& mimeType, PluginPackage* plugin)
       
   273 {
       
   274     if (!plugin || plugin->mimeToExtensions().contains(mimeType))
       
   275         m_preferredPlugins.set(mimeType.lower(), plugin);
       
   276 }
       
   277 
       
   278 void PluginDatabase::getDeletedPlugins(PluginSet& plugins) const
       
   279 {
       
   280     PluginSet::const_iterator end = m_plugins.end();
       
   281     for (PluginSet::const_iterator it = m_plugins.begin(); it != end; ++it) {
       
   282         if (!fileExists((*it)->path()))
       
   283             plugins.add(*it);
       
   284     }
       
   285 }
       
   286 
       
   287 bool PluginDatabase::add(PassRefPtr<PluginPackage> prpPackage)
       
   288 {
       
   289     ASSERT_ARG(prpPackage, prpPackage);
       
   290 
       
   291     RefPtr<PluginPackage> package = prpPackage;
       
   292 
       
   293     if (!m_plugins.add(package).second)
       
   294         return false;
       
   295 
       
   296     m_pluginsByPath.add(package->path(), package);
       
   297     return true;
       
   298 }
       
   299 
       
   300 void PluginDatabase::remove(PluginPackage* package)
       
   301 {
       
   302     MIMEToExtensionsMap::const_iterator it = package->mimeToExtensions().begin();
       
   303     MIMEToExtensionsMap::const_iterator end = package->mimeToExtensions().end();
       
   304     for ( ; it != end; ++it) {
       
   305         PluginPackageByNameMap::iterator packageInMap = m_preferredPlugins.find(it->first);
       
   306         if (packageInMap != m_preferredPlugins.end() && packageInMap->second == package)
       
   307             m_preferredPlugins.remove(packageInMap);
       
   308     }
       
   309 
       
   310     m_plugins.remove(package);
       
   311     m_pluginsByPath.remove(package->path());
       
   312 }
       
   313 
       
   314 void PluginDatabase::clear()
       
   315 {
       
   316     m_plugins.clear();
       
   317     m_pluginsByPath.clear();
       
   318     m_pluginPathsWithTimes.clear();
       
   319     m_registeredMIMETypes.clear();
       
   320     m_preferredPlugins.clear();
       
   321 }
       
   322 
       
   323 #if (!PLATFORM(SYMBIAN)) && (!PLATFORM(WIN_OS) || PLATFORM(WX))
       
   324 // For Safari/Win the following three methods are implemented
       
   325 // in PluginDatabaseWin.cpp, but if we can use WebCore constructs
       
   326 // for the logic we should perhaps move it here under XP_WIN?
       
   327 
       
   328 Vector<String> PluginDatabase::defaultPluginDirectories()
       
   329 {
       
   330     Vector<String> paths;
       
   331 
       
   332     // Add paths specific to each platform
       
   333 #if defined(XP_UNIX)
       
   334     String userPluginPath = homeDirectoryPath();
       
   335     userPluginPath.append(String("/.mozilla/plugins"));
       
   336     paths.append(userPluginPath);
       
   337 
       
   338     userPluginPath = homeDirectoryPath();
       
   339     userPluginPath.append(String("/.netscape/plugins"));
       
   340     paths.append(userPluginPath);
       
   341 
       
   342     paths.append("/usr/lib/browser/plugins");
       
   343     paths.append("/usr/local/lib/mozilla/plugins");
       
   344     paths.append("/usr/lib/firefox/plugins");
       
   345     paths.append("/usr/lib64/browser-plugins");
       
   346     paths.append("/usr/lib/browser-plugins");
       
   347     paths.append("/usr/lib/mozilla/plugins");
       
   348     paths.append("/usr/local/netscape/plugins");
       
   349     paths.append("/opt/mozilla/plugins");
       
   350     paths.append("/opt/mozilla/lib/plugins");
       
   351     paths.append("/opt/netscape/plugins");
       
   352     paths.append("/opt/netscape/communicator/plugins");
       
   353     paths.append("/usr/lib/netscape/plugins");
       
   354     paths.append("/usr/lib/netscape/plugins-libc5");
       
   355     paths.append("/usr/lib/netscape/plugins-libc6");
       
   356     paths.append("/usr/lib64/netscape/plugins");
       
   357     paths.append("/usr/lib64/mozilla/plugins");
       
   358     paths.append("/usr/lib/nsbrowser/plugins");
       
   359     paths.append("/usr/lib64/nsbrowser/plugins");
       
   360 
       
   361     String mozHome(getenv("MOZILLA_HOME"));
       
   362     mozHome.append("/plugins");
       
   363     paths.append(mozHome);
       
   364 
       
   365     Vector<String> mozPaths;
       
   366     String mozPath(getenv("MOZ_PLUGIN_PATH"));
       
   367     mozPath.split(UChar(':'), /* allowEmptyEntries */ false, mozPaths);
       
   368     paths.append(mozPaths);
       
   369 #elif defined(XP_MACOSX)
       
   370     String userPluginPath = homeDirectoryPath();
       
   371     userPluginPath.append(String("/Library/Internet Plug-Ins"));
       
   372     paths.append(userPluginPath);
       
   373     paths.append("/Library/Internet Plug-Ins");
       
   374 #elif defined(XP_WIN)
       
   375     String userPluginPath = homeDirectoryPath();
       
   376     userPluginPath.append(String("\\Application Data\\Mozilla\\plugins"));
       
   377     paths.append(userPluginPath);
       
   378 #endif
       
   379 
       
   380     // Add paths specific to each port
       
   381 #if PLATFORM(QT)
       
   382     Vector<String> qtPaths;
       
   383     String qtPath(getenv("QTWEBKIT_PLUGIN_PATH"));
       
   384     qtPath.split(UChar(':'), /* allowEmptyEntries */ false, qtPaths);
       
   385     paths.append(qtPaths);
       
   386 #endif
       
   387 
       
   388     return paths;
       
   389 }
       
   390 
       
   391 bool PluginDatabase::isPreferredPluginDirectory(const String& path)
       
   392 {
       
   393     String preferredPath = homeDirectoryPath();
       
   394 
       
   395 #if defined(XP_UNIX)
       
   396     preferredPath.append(String("/.mozilla/plugins"));
       
   397 #elif defined(XP_MACOSX)
       
   398     preferredPath.append(String("/Library/Internet Plug-Ins"));
       
   399 #elif defined(XP_WIN)
       
   400     preferredPath.append(String("\\Application Data\\Mozilla\\plugins"));
       
   401 #endif
       
   402 
       
   403     // TODO: We should normalize the path before doing a comparison.
       
   404     return path == preferredPath;
       
   405 }
       
   406 
       
   407 void PluginDatabase::getPluginPathsInDirectories(HashSet<String>& paths) const
       
   408 {
       
   409     // FIXME: This should be a case insensitive set.
       
   410     HashSet<String> uniqueFilenames;
       
   411 
       
   412 #if defined(XP_UNIX)
       
   413     String fileNameFilter("*.so");
       
   414 #else
       
   415     String fileNameFilter("");
       
   416 #endif
       
   417 
       
   418     Vector<String>::const_iterator dirsEnd = m_pluginDirectories.end();
       
   419     for (Vector<String>::const_iterator dIt = m_pluginDirectories.begin(); dIt != dirsEnd; ++dIt) {
       
   420         Vector<String> pluginPaths = listDirectory(*dIt, fileNameFilter);
       
   421         Vector<String>::const_iterator pluginsEnd = pluginPaths.end();
       
   422         for (Vector<String>::const_iterator pIt = pluginPaths.begin(); pIt != pluginsEnd; ++pIt) {
       
   423             if (!fileExists(*pIt))
       
   424                 continue;
       
   425 
       
   426             paths.add(*pIt);
       
   427         }
       
   428     }
       
   429 }
       
   430 
       
   431 #endif // !PLATFORM(SYMBIAN) && !PLATFORM(WIN_OS)
       
   432 
       
   433 }