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