Home | History | Annotate | Download | only in plugins
      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 #if ENABLE(NETSCAPE_PLUGIN_METADATA_CACHE)
     34 #include "FileSystem.h"
     35 #endif
     36 #include <stdlib.h>
     37 #include <wtf/text/CString.h>
     38 
     39 #if PLATFORM(ANDROID)
     40 #include "JavaSharedClient.h"
     41 #include "PluginClient.h"
     42 #endif
     43 
     44 namespace WebCore {
     45 
     46 typedef HashMap<String, RefPtr<PluginPackage> > PluginPackageByNameMap;
     47 
     48 #if ENABLE(NETSCAPE_PLUGIN_METADATA_CACHE)
     49 static const size_t maximumPersistentPluginMetadataCacheSize = 32768;
     50 
     51 static bool gPersistentPluginMetadataCacheIsEnabled;
     52 
     53 String& persistentPluginMetadataCachePath()
     54 {
     55     DEFINE_STATIC_LOCAL(String, cachePath, ());
     56     return cachePath;
     57 }
     58 #endif
     59 
     60 PluginDatabase::PluginDatabase()
     61 #if ENABLE(NETSCAPE_PLUGIN_METADATA_CACHE)
     62     : m_persistentMetadataCacheIsLoaded(false)
     63 #endif
     64 {
     65 }
     66 
     67 PluginDatabase* PluginDatabase::installedPlugins(bool populate)
     68 {
     69     static PluginDatabase* plugins = 0;
     70 
     71     if (!plugins) {
     72         plugins = new PluginDatabase;
     73 
     74         if (populate) {
     75             plugins->setPluginDirectories(PluginDatabase::defaultPluginDirectories());
     76             plugins->refresh();
     77         }
     78     }
     79 
     80     return plugins;
     81 }
     82 
     83 bool PluginDatabase::isMIMETypeRegistered(const String& mimeType)
     84 {
     85     if (mimeType.isNull())
     86         return false;
     87     if (m_registeredMIMETypes.contains(mimeType))
     88         return true;
     89     // No plugin was found, try refreshing the database and searching again
     90     return (refresh() && m_registeredMIMETypes.contains(mimeType));
     91 }
     92 
     93 void PluginDatabase::addExtraPluginDirectory(const String& directory)
     94 {
     95     m_pluginDirectories.append(directory);
     96     refresh();
     97 }
     98 
     99 bool PluginDatabase::refresh()
    100 {
    101 #if ENABLE(NETSCAPE_PLUGIN_METADATA_CACHE)
    102     if (!m_persistentMetadataCacheIsLoaded)
    103         loadPersistentMetadataCache();
    104 #endif
    105     bool pluginSetChanged = false;
    106 
    107     if (!m_plugins.isEmpty()) {
    108         PluginSet pluginsToUnload;
    109         getDeletedPlugins(pluginsToUnload);
    110 
    111         // Unload plugins
    112         PluginSet::const_iterator end = pluginsToUnload.end();
    113         for (PluginSet::const_iterator it = pluginsToUnload.begin(); it != end; ++it)
    114             remove(it->get());
    115 
    116         pluginSetChanged = !pluginsToUnload.isEmpty();
    117     }
    118 
    119     HashSet<String> paths;
    120     getPluginPathsInDirectories(paths);
    121 
    122     HashMap<String, time_t> pathsWithTimes;
    123 
    124     // We should only skip unchanged files if we didn't remove any plugins above. If we did remove
    125     // any plugins, we need to look at every plugin file so that, e.g., if the user has two versions
    126     // of RealPlayer installed and just removed the newer one, we'll pick up the older one.
    127     bool shouldSkipUnchangedFiles = !pluginSetChanged;
    128 
    129     HashSet<String>::const_iterator pathsEnd = paths.end();
    130     for (HashSet<String>::const_iterator it = paths.begin(); it != pathsEnd; ++it) {
    131         time_t lastModified;
    132         if (!getFileModificationTime(*it, lastModified))
    133             continue;
    134 
    135         pathsWithTimes.add(*it, lastModified);
    136 
    137         // If the path's timestamp hasn't changed since the last time we ran refresh(), we don't have to do anything.
    138         if (shouldSkipUnchangedFiles && m_pluginPathsWithTimes.get(*it) == lastModified)
    139             continue;
    140 
    141         if (RefPtr<PluginPackage> oldPackage = m_pluginsByPath.get(*it)) {
    142             ASSERT(!shouldSkipUnchangedFiles || oldPackage->lastModified() != lastModified);
    143             remove(oldPackage.get());
    144         }
    145 
    146         RefPtr<PluginPackage> package = PluginPackage::createPackage(*it, lastModified);
    147         if (package && add(package.release()))
    148             pluginSetChanged = true;
    149     }
    150 
    151     // Cache all the paths we found with their timestamps for next time.
    152     pathsWithTimes.swap(m_pluginPathsWithTimes);
    153 
    154     if (!pluginSetChanged)
    155         return false;
    156 
    157 #if ENABLE(NETSCAPE_PLUGIN_METADATA_CACHE)
    158     updatePersistentMetadataCache();
    159 #endif
    160 
    161     m_registeredMIMETypes.clear();
    162 
    163     // Register plug-in MIME types
    164     PluginSet::const_iterator end = m_plugins.end();
    165     for (PluginSet::const_iterator it = m_plugins.begin(); it != end; ++it) {
    166         // Get MIME types
    167         MIMEToDescriptionsMap::const_iterator map_it = (*it)->mimeToDescriptions().begin();
    168         MIMEToDescriptionsMap::const_iterator map_end = (*it)->mimeToDescriptions().end();
    169         for (; map_it != map_end; ++map_it)
    170             m_registeredMIMETypes.add(map_it->first);
    171     }
    172 
    173     return true;
    174 }
    175 
    176 Vector<PluginPackage*> PluginDatabase::plugins() const
    177 {
    178     Vector<PluginPackage*> result;
    179 
    180     PluginSet::const_iterator end = m_plugins.end();
    181     for (PluginSet::const_iterator it = m_plugins.begin(); it != end; ++it)
    182         result.append((*it).get());
    183 
    184     return result;
    185 }
    186 
    187 int PluginDatabase::preferredPluginCompare(const void* a, const void* b)
    188 {
    189     PluginPackage* pluginA = *static_cast<PluginPackage* const*>(a);
    190     PluginPackage* pluginB = *static_cast<PluginPackage* const*>(b);
    191 
    192     return pluginA->compare(*pluginB);
    193 }
    194 
    195 PluginPackage* PluginDatabase::pluginForMIMEType(const String& mimeType)
    196 {
    197     if (mimeType.isEmpty())
    198         return 0;
    199 
    200     String key = mimeType.lower();
    201     PluginSet::const_iterator end = m_plugins.end();
    202     PluginPackage* preferredPlugin = m_preferredPlugins.get(key).get();
    203     if (preferredPlugin
    204         && preferredPlugin->isEnabled()
    205         && preferredPlugin->mimeToDescriptions().contains(key)) {
    206         return preferredPlugin;
    207     }
    208 
    209     Vector<PluginPackage*, 2> pluginChoices;
    210 
    211     for (PluginSet::const_iterator it = m_plugins.begin(); it != end; ++it) {
    212         PluginPackage* plugin = (*it).get();
    213 
    214         if (!plugin->isEnabled())
    215             continue;
    216 
    217         if (plugin->mimeToDescriptions().contains(key)) {
    218 #if ENABLE(NETSCAPE_PLUGIN_METADATA_CACHE)
    219             if (!plugin->ensurePluginLoaded())
    220                 continue;
    221 #endif
    222             pluginChoices.append(plugin);
    223         }
    224     }
    225 
    226     if (pluginChoices.isEmpty())
    227         return 0;
    228 
    229     qsort(pluginChoices.data(), pluginChoices.size(), sizeof(PluginPackage*), PluginDatabase::preferredPluginCompare);
    230 
    231     return pluginChoices[0];
    232 }
    233 
    234 String PluginDatabase::MIMETypeForExtension(const String& extension) const
    235 {
    236     if (extension.isEmpty())
    237         return String();
    238 
    239     PluginSet::const_iterator end = m_plugins.end();
    240     String mimeType;
    241     Vector<PluginPackage*, 2> pluginChoices;
    242     HashMap<PluginPackage*, String> mimeTypeForPlugin;
    243 
    244     for (PluginSet::const_iterator it = m_plugins.begin(); it != end; ++it) {
    245         if (!(*it)->isEnabled())
    246             continue;
    247 
    248         MIMEToExtensionsMap::const_iterator mime_end = (*it)->mimeToExtensions().end();
    249 
    250         for (MIMEToExtensionsMap::const_iterator mime_it = (*it)->mimeToExtensions().begin(); mime_it != mime_end; ++mime_it) {
    251             mimeType = mime_it->first;
    252             PluginPackage* preferredPlugin = m_preferredPlugins.get(mimeType).get();
    253             const Vector<String>& extensions = mime_it->second;
    254             bool foundMapping = false;
    255             for (unsigned i = 0; i < extensions.size(); i++) {
    256                 if (equalIgnoringCase(extensions[i], extension)) {
    257                     PluginPackage* plugin = (*it).get();
    258 
    259                     if (preferredPlugin && PluginPackage::equal(*plugin, *preferredPlugin))
    260                         return mimeType;
    261 
    262 #if ENABLE(NETSCAPE_PLUGIN_METADATA_CACHE)
    263                     if (!plugin->ensurePluginLoaded())
    264                         continue;
    265 #endif
    266                     pluginChoices.append(plugin);
    267                     mimeTypeForPlugin.add(plugin, mimeType);
    268                     foundMapping = true;
    269                     break;
    270                 }
    271             }
    272             if (foundMapping)
    273                 break;
    274         }
    275     }
    276 
    277     if (pluginChoices.isEmpty())
    278         return String();
    279 
    280     qsort(pluginChoices.data(), pluginChoices.size(), sizeof(PluginPackage*), PluginDatabase::preferredPluginCompare);
    281 
    282     return mimeTypeForPlugin.get(pluginChoices[0]);
    283 }
    284 
    285 PluginPackage* PluginDatabase::findPlugin(const KURL& url, String& mimeType)
    286 {
    287     if (!mimeType.isEmpty())
    288         return pluginForMIMEType(mimeType);
    289 
    290     String filename = url.lastPathComponent();
    291     if (filename.endsWith("/"))
    292         return 0;
    293 
    294     int extensionPos = filename.reverseFind('.');
    295     if (extensionPos == -1)
    296         return 0;
    297 
    298     String mimeTypeForExtension = MIMETypeForExtension(filename.substring(extensionPos + 1));
    299     PluginPackage* plugin = pluginForMIMEType(mimeTypeForExtension);
    300     if (!plugin) {
    301         // FIXME: if no plugin could be found, query Windows for the mime type
    302         // corresponding to the extension.
    303         return 0;
    304     }
    305 
    306     mimeType = mimeTypeForExtension;
    307     return plugin;
    308 }
    309 
    310 void PluginDatabase::setPreferredPluginForMIMEType(const String& mimeType, PluginPackage* plugin)
    311 {
    312     if (!plugin || plugin->mimeToExtensions().contains(mimeType))
    313         m_preferredPlugins.set(mimeType.lower(), plugin);
    314 }
    315 
    316 void PluginDatabase::getDeletedPlugins(PluginSet& plugins) const
    317 {
    318     PluginSet::const_iterator end = m_plugins.end();
    319     for (PluginSet::const_iterator it = m_plugins.begin(); it != end; ++it) {
    320         if (!fileExists((*it)->path()))
    321             plugins.add(*it);
    322     }
    323 }
    324 
    325 bool PluginDatabase::add(PassRefPtr<PluginPackage> prpPackage)
    326 {
    327     ASSERT_ARG(prpPackage, prpPackage);
    328 
    329     RefPtr<PluginPackage> package = prpPackage;
    330 
    331     if (!m_plugins.add(package).second)
    332         return false;
    333 
    334     m_pluginsByPath.add(package->path(), package);
    335     return true;
    336 }
    337 
    338 void PluginDatabase::remove(PluginPackage* package)
    339 {
    340     MIMEToExtensionsMap::const_iterator it = package->mimeToExtensions().begin();
    341     MIMEToExtensionsMap::const_iterator end = package->mimeToExtensions().end();
    342     for ( ; it != end; ++it) {
    343         PluginPackageByNameMap::iterator packageInMap = m_preferredPlugins.find(it->first);
    344         if (packageInMap != m_preferredPlugins.end() && packageInMap->second == package)
    345             m_preferredPlugins.remove(packageInMap);
    346     }
    347 
    348     m_plugins.remove(package);
    349     m_pluginsByPath.remove(package->path());
    350 }
    351 
    352 void PluginDatabase::clear()
    353 {
    354     m_plugins.clear();
    355     m_pluginsByPath.clear();
    356     m_pluginPathsWithTimes.clear();
    357     m_registeredMIMETypes.clear();
    358     m_preferredPlugins.clear();
    359 #if ENABLE(NETSCAPE_PLUGIN_METADATA_CACHE)
    360     m_persistentMetadataCacheIsLoaded = false;
    361 #endif
    362 }
    363 
    364 #if (!OS(WINCE)) && (!OS(SYMBIAN)) && (!OS(WINDOWS) || !ENABLE(NETSCAPE_PLUGIN_API))
    365 // For Safari/Win the following three methods are implemented
    366 // in PluginDatabaseWin.cpp, but if we can use WebCore constructs
    367 // for the logic we should perhaps move it here under XP_WIN?
    368 
    369 Vector<String> PluginDatabase::defaultPluginDirectories()
    370 {
    371     Vector<String> paths;
    372 
    373     // Add paths specific to each platform
    374 #if defined(XP_UNIX)
    375     String userPluginPath = homeDirectoryPath();
    376     userPluginPath.append(String("/.mozilla/plugins"));
    377     paths.append(userPluginPath);
    378 
    379     userPluginPath = homeDirectoryPath();
    380     userPluginPath.append(String("/.netscape/plugins"));
    381     paths.append(userPluginPath);
    382 
    383     paths.append("/usr/lib/browser/plugins");
    384     paths.append("/usr/local/lib/mozilla/plugins");
    385     paths.append("/usr/lib/firefox/plugins");
    386     paths.append("/usr/lib64/browser-plugins");
    387     paths.append("/usr/lib/browser-plugins");
    388     paths.append("/usr/lib/mozilla/plugins");
    389     paths.append("/usr/local/netscape/plugins");
    390     paths.append("/opt/mozilla/plugins");
    391     paths.append("/opt/mozilla/lib/plugins");
    392     paths.append("/opt/netscape/plugins");
    393     paths.append("/opt/netscape/communicator/plugins");
    394     paths.append("/usr/lib/netscape/plugins");
    395     paths.append("/usr/lib/netscape/plugins-libc5");
    396     paths.append("/usr/lib/netscape/plugins-libc6");
    397     paths.append("/usr/lib64/netscape/plugins");
    398     paths.append("/usr/lib64/mozilla/plugins");
    399     paths.append("/usr/lib/nsbrowser/plugins");
    400     paths.append("/usr/lib64/nsbrowser/plugins");
    401 
    402     String mozHome(getenv("MOZILLA_HOME"));
    403     mozHome.append("/plugins");
    404     paths.append(mozHome);
    405 
    406     Vector<String> mozPaths;
    407     String mozPath(getenv("MOZ_PLUGIN_PATH"));
    408     mozPath.split(UChar(':'), /* allowEmptyEntries */ false, mozPaths);
    409     paths.append(mozPaths);
    410 #elif defined(XP_MACOSX)
    411     String userPluginPath = homeDirectoryPath();
    412     userPluginPath.append(String("/Library/Internet Plug-Ins"));
    413     paths.append(userPluginPath);
    414     paths.append("/Library/Internet Plug-Ins");
    415 #elif defined(XP_WIN)
    416     String userPluginPath = homeDirectoryPath();
    417     userPluginPath.append(String("\\Application Data\\Mozilla\\plugins"));
    418     paths.append(userPluginPath);
    419 #endif
    420 
    421     // Add paths specific to each port
    422 #if PLATFORM(QT)
    423     Vector<String> qtPaths;
    424     String qtPath(qgetenv("QTWEBKIT_PLUGIN_PATH").constData());
    425     qtPath.split(UChar(':'), /* allowEmptyEntries */ false, qtPaths);
    426     paths.append(qtPaths);
    427 #endif
    428 
    429 #if PLATFORM(ANDROID)
    430     if (android::JavaSharedClient::GetPluginClient())
    431         return android::JavaSharedClient::GetPluginClient()->getPluginDirectories();
    432 #endif
    433 
    434     return paths;
    435 }
    436 
    437 bool PluginDatabase::isPreferredPluginDirectory(const String& path)
    438 {
    439     String preferredPath = homeDirectoryPath();
    440 
    441 #if defined(XP_UNIX)
    442     preferredPath.append(String("/.mozilla/plugins"));
    443 #elif defined(XP_MACOSX)
    444     preferredPath.append(String("/Library/Internet Plug-Ins"));
    445 #elif defined(XP_WIN)
    446     preferredPath.append(String("\\Application Data\\Mozilla\\plugins"));
    447 #endif
    448 
    449     // TODO: We should normalize the path before doing a comparison.
    450     return path == preferredPath;
    451 }
    452 
    453 void PluginDatabase::getPluginPathsInDirectories(HashSet<String>& paths) const
    454 {
    455     // FIXME: This should be a case insensitive set.
    456     HashSet<String> uniqueFilenames;
    457 
    458 #if defined(XP_UNIX) || defined(ANDROID)
    459     String fileNameFilter("*.so");
    460 #else
    461     String fileNameFilter("");
    462 #endif
    463 
    464     Vector<String>::const_iterator dirsEnd = m_pluginDirectories.end();
    465     for (Vector<String>::const_iterator dIt = m_pluginDirectories.begin(); dIt != dirsEnd; ++dIt) {
    466         Vector<String> pluginPaths = listDirectory(*dIt, fileNameFilter);
    467         Vector<String>::const_iterator pluginsEnd = pluginPaths.end();
    468         for (Vector<String>::const_iterator pIt = pluginPaths.begin(); pIt != pluginsEnd; ++pIt) {
    469             if (!fileExists(*pIt))
    470                 continue;
    471 
    472             paths.add(*pIt);
    473         }
    474     }
    475 }
    476 
    477 #endif // !OS(SYMBIAN) && !OS(WINDOWS)
    478 
    479 #if ENABLE(NETSCAPE_PLUGIN_METADATA_CACHE)
    480 
    481 static void fillBufferWithContentsOfFile(PlatformFileHandle file, Vector<char>& buffer)
    482 {
    483     size_t bufferSize = 0;
    484     size_t bufferCapacity = 1024;
    485     buffer.resize(bufferCapacity);
    486 
    487     do {
    488         bufferSize += readFromFile(file, buffer.data() + bufferSize, bufferCapacity - bufferSize);
    489         if (bufferSize == bufferCapacity) {
    490             if (bufferCapacity < maximumPersistentPluginMetadataCacheSize) {
    491                 bufferCapacity *= 2;
    492                 buffer.resize(bufferCapacity);
    493             } else {
    494                 buffer.clear();
    495                 return;
    496             }
    497         } else
    498             break;
    499     } while (true);
    500 
    501     buffer.shrink(bufferSize);
    502 }
    503 
    504 static bool readUTF8String(String& resultString, char*& start, const char* end)
    505 {
    506     if (start >= end)
    507         return false;
    508 
    509     int len = strlen(start);
    510     resultString = String::fromUTF8(start, len);
    511     start += len + 1;
    512 
    513     return true;
    514 }
    515 
    516 static bool readTime(time_t& resultTime, char*& start, const char* end)
    517 {
    518     if (start + sizeof(time_t) >= end)
    519         return false;
    520 
    521     resultTime = *reinterpret_cast_ptr<time_t*>(start);
    522     start += sizeof(time_t);
    523 
    524     return true;
    525 }
    526 
    527 static const char schemaVersion = '1';
    528 static const char persistentPluginMetadataCacheFilename[] = "PluginMetadataCache.bin";
    529 
    530 void PluginDatabase::loadPersistentMetadataCache()
    531 {
    532     if (!isPersistentMetadataCacheEnabled() || persistentMetadataCachePath().isEmpty())
    533         return;
    534 
    535     PlatformFileHandle file;
    536     String absoluteCachePath = pathByAppendingComponent(persistentMetadataCachePath(), persistentPluginMetadataCacheFilename);
    537     file = openFile(absoluteCachePath, OpenForRead);
    538 
    539     if (!isHandleValid(file))
    540         return;
    541 
    542     // Mark cache as loaded regardless of success or failure. If
    543     // there's error in the cache, we won't try to load it anymore.
    544     m_persistentMetadataCacheIsLoaded = true;
    545 
    546     Vector<char> fileContents;
    547     fillBufferWithContentsOfFile(file, fileContents);
    548     closeFile(file);
    549 
    550     if (fileContents.size() < 2 || fileContents.first() != schemaVersion || fileContents.last() != '\0') {
    551         LOG_ERROR("Unable to read plugin metadata cache: corrupt schema");
    552         deleteFile(absoluteCachePath);
    553         return;
    554     }
    555 
    556     char* bufferPos = fileContents.data() + 1;
    557     char* end = fileContents.data() + fileContents.size();
    558 
    559     PluginSet cachedPlugins;
    560     HashMap<String, time_t> cachedPluginPathsWithTimes;
    561     HashMap<String, RefPtr<PluginPackage> > cachedPluginsByPath;
    562 
    563     while (bufferPos < end) {
    564         String path;
    565         time_t lastModified;
    566         String name;
    567         String desc;
    568         String mimeDesc;
    569         if (!(readUTF8String(path, bufferPos, end)
    570               && readTime(lastModified, bufferPos, end)
    571               && readUTF8String(name, bufferPos, end)
    572               && readUTF8String(desc, bufferPos, end)
    573               && readUTF8String(mimeDesc, bufferPos, end))) {
    574             LOG_ERROR("Unable to read plugin metadata cache: corrupt data");
    575             deleteFile(absoluteCachePath);
    576             return;
    577         }
    578 
    579         // Skip metadata that points to plugins from directories that
    580         // are not part of plugin directory list anymore.
    581         String pluginDirectoryName = directoryName(path);
    582         if (m_pluginDirectories.find(pluginDirectoryName) == WTF::notFound)
    583             continue;
    584 
    585         RefPtr<PluginPackage> package = PluginPackage::createPackageFromCache(path, lastModified, name, desc, mimeDesc);
    586 
    587         if (package && cachedPlugins.add(package).second) {
    588             cachedPluginPathsWithTimes.add(package->path(), package->lastModified());
    589             cachedPluginsByPath.add(package->path(), package);
    590         }
    591     }
    592 
    593     m_plugins.swap(cachedPlugins);
    594     m_pluginsByPath.swap(cachedPluginsByPath);
    595     m_pluginPathsWithTimes.swap(cachedPluginPathsWithTimes);
    596 }
    597 
    598 static bool writeUTF8String(PlatformFileHandle file, const String& string)
    599 {
    600     CString utf8String = string.utf8();
    601     int length = utf8String.length() + 1;
    602     return writeToFile(file, utf8String.data(), length) == length;
    603 }
    604 
    605 static bool writeTime(PlatformFileHandle file, const time_t& time)
    606 {
    607     return writeToFile(file, reinterpret_cast<const char*>(&time), sizeof(time_t)) == sizeof(time_t);
    608 }
    609 
    610 void PluginDatabase::updatePersistentMetadataCache()
    611 {
    612     if (!isPersistentMetadataCacheEnabled() || persistentMetadataCachePath().isEmpty())
    613         return;
    614 
    615     makeAllDirectories(persistentMetadataCachePath());
    616     String absoluteCachePath = pathByAppendingComponent(persistentMetadataCachePath(), persistentPluginMetadataCacheFilename);
    617     deleteFile(absoluteCachePath);
    618 
    619     if (m_plugins.isEmpty())
    620         return;
    621 
    622     PlatformFileHandle file;
    623     file = openFile(absoluteCachePath, OpenForWrite);
    624 
    625     if (!isHandleValid(file)) {
    626         LOG_ERROR("Unable to open plugin metadata cache for saving");
    627         return;
    628     }
    629 
    630     char localSchemaVersion = schemaVersion;
    631     if (writeToFile(file, &localSchemaVersion, 1) != 1) {
    632         LOG_ERROR("Unable to write plugin metadata cache schema");
    633         closeFile(file);
    634         deleteFile(absoluteCachePath);
    635         return;
    636     }
    637 
    638     PluginSet::const_iterator end = m_plugins.end();
    639     for (PluginSet::const_iterator it = m_plugins.begin(); it != end; ++it) {
    640         if (!(writeUTF8String(file, (*it)->path())
    641               && writeTime(file, (*it)->lastModified())
    642               && writeUTF8String(file, (*it)->name())
    643               && writeUTF8String(file, (*it)->description())
    644               && writeUTF8String(file, (*it)->fullMIMEDescription()))) {
    645             LOG_ERROR("Unable to write plugin metadata to cache");
    646             closeFile(file);
    647             deleteFile(absoluteCachePath);
    648             return;
    649         }
    650     }
    651 
    652     closeFile(file);
    653 }
    654 
    655 bool PluginDatabase::isPersistentMetadataCacheEnabled()
    656 {
    657     return gPersistentPluginMetadataCacheIsEnabled;
    658 }
    659 
    660 void PluginDatabase::setPersistentMetadataCacheEnabled(bool isEnabled)
    661 {
    662     gPersistentPluginMetadataCacheIsEnabled = isEnabled;
    663 }
    664 
    665 String PluginDatabase::persistentMetadataCachePath()
    666 {
    667     return WebCore::persistentPluginMetadataCachePath();
    668 }
    669 
    670 void PluginDatabase::setPersistentMetadataCachePath(const String& persistentMetadataCachePath)
    671 {
    672     WebCore::persistentPluginMetadataCachePath() = persistentMetadataCachePath;
    673 }
    674 #endif
    675 }
    676