summaryrefslogtreecommitdiffstats
path: root/Source/WebCore/plugins/PluginDatabase.cpp
diff options
context:
space:
mode:
authorSteve Block <steveblock@google.com>2011-05-06 11:45:16 +0100
committerSteve Block <steveblock@google.com>2011-05-12 13:44:10 +0100
commitcad810f21b803229eb11403f9209855525a25d57 (patch)
tree29a6fd0279be608e0fe9ffe9841f722f0f4e4269 /Source/WebCore/plugins/PluginDatabase.cpp
parent121b0cf4517156d0ac5111caf9830c51b69bae8f (diff)
downloadexternal_webkit-cad810f21b803229eb11403f9209855525a25d57.zip
external_webkit-cad810f21b803229eb11403f9209855525a25d57.tar.gz
external_webkit-cad810f21b803229eb11403f9209855525a25d57.tar.bz2
Merge WebKit at r75315: Initial merge by git.
Change-Id: I570314b346ce101c935ed22a626b48c2af266b84
Diffstat (limited to 'Source/WebCore/plugins/PluginDatabase.cpp')
-rw-r--r--Source/WebCore/plugins/PluginDatabase.cpp675
1 files changed, 675 insertions, 0 deletions
diff --git a/Source/WebCore/plugins/PluginDatabase.cpp b/Source/WebCore/plugins/PluginDatabase.cpp
new file mode 100644
index 0000000..b9e154a
--- /dev/null
+++ b/Source/WebCore/plugins/PluginDatabase.cpp
@@ -0,0 +1,675 @@
+/*
+ * 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 "PluginPackage.h"
+#if ENABLE(NETSCAPE_PLUGIN_METADATA_CACHE)
+#include "FileSystem.h"
+#endif
+#include <stdlib.h>
+#include <wtf/text/CString.h>
+
+#if PLATFORM(ANDROID)
+#include "JavaSharedClient.h"
+#include "PluginClient.h"
+#endif
+
+namespace WebCore {
+
+typedef HashMap<String, RefPtr<PluginPackage> > PluginPackageByNameMap;
+
+#if ENABLE(NETSCAPE_PLUGIN_METADATA_CACHE)
+static const size_t maximumPersistentPluginMetadataCacheSize = 32768;
+
+static bool gPersistentPluginMetadataCacheIsEnabled;
+
+String& persistentPluginMetadataCachePath()
+{
+ DEFINE_STATIC_LOCAL(String, cachePath, ());
+ return cachePath;
+}
+#endif
+
+PluginDatabase::PluginDatabase()
+#if ENABLE(NETSCAPE_PLUGIN_METADATA_CACHE)
+ : m_persistentMetadataCacheIsLoaded(false)
+#endif
+{
+}
+
+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()
+{
+#if ENABLE(NETSCAPE_PLUGIN_METADATA_CACHE)
+ if (!m_persistentMetadataCacheIsLoaded)
+ loadPersistentMetadataCache();
+#endif
+ 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());
+ }
+
+ RefPtr<PluginPackage> package = PluginPackage::createPackage(*it, lastModified);
+ if (package && 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;
+
+#if ENABLE(NETSCAPE_PLUGIN_METADATA_CACHE)
+ updatePersistentMetadataCache();
+#endif
+
+ 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)) {
+#if ENABLE(NETSCAPE_PLUGIN_METADATA_CACHE)
+ if (!plugin->ensurePluginLoaded())
+ continue;
+#endif
+ 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;
+
+#if ENABLE(NETSCAPE_PLUGIN_METADATA_CACHE)
+ if (!plugin->ensurePluginLoaded())
+ continue;
+#endif
+ 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)
+{
+ if (!mimeType.isEmpty())
+ return pluginForMIMEType(mimeType);
+
+ String filename = url.lastPathComponent();
+ if (filename.endsWith("/"))
+ return 0;
+
+ int extensionPos = filename.reverseFind('.');
+ if (extensionPos == -1)
+ return 0;
+
+ String mimeTypeForExtension = MIMETypeForExtension(filename.substring(extensionPos + 1));
+ PluginPackage* plugin = pluginForMIMEType(mimeTypeForExtension);
+ if (!plugin) {
+ // FIXME: if no plugin could be found, query Windows for the mime type
+ // corresponding to the extension.
+ return 0;
+ }
+
+ mimeType = mimeTypeForExtension;
+ 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 ENABLE(NETSCAPE_PLUGIN_METADATA_CACHE)
+ m_persistentMetadataCacheIsLoaded = false;
+#endif
+}
+
+#if (!OS(WINCE)) && (!OS(SYMBIAN)) && (!OS(WINDOWS) || !ENABLE(NETSCAPE_PLUGIN_API))
+// 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(qgetenv("QTWEBKIT_PLUGIN_PATH").constData());
+ 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<String>& paths) const
+{
+ // FIXME: This should be a case insensitive set.
+ HashSet<String> uniqueFilenames;
+
+#if defined(XP_UNIX) || defined(ANDROID)
+ 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 // !OS(SYMBIAN) && !OS(WINDOWS)
+
+#if ENABLE(NETSCAPE_PLUGIN_METADATA_CACHE)
+
+static void fillBufferWithContentsOfFile(PlatformFileHandle file, Vector<char>& buffer)
+{
+ size_t bufferSize = 0;
+ size_t bufferCapacity = 1024;
+ buffer.resize(bufferCapacity);
+
+ do {
+ bufferSize += readFromFile(file, buffer.data() + bufferSize, bufferCapacity - bufferSize);
+ if (bufferSize == bufferCapacity) {
+ if (bufferCapacity < maximumPersistentPluginMetadataCacheSize) {
+ bufferCapacity *= 2;
+ buffer.resize(bufferCapacity);
+ } else {
+ buffer.clear();
+ return;
+ }
+ } else
+ break;
+ } while (true);
+
+ buffer.shrink(bufferSize);
+}
+
+static bool readUTF8String(String& resultString, char*& start, const char* end)
+{
+ if (start >= end)
+ return false;
+
+ int len = strlen(start);
+ resultString = String::fromUTF8(start, len);
+ start += len + 1;
+
+ return true;
+}
+
+static bool readTime(time_t& resultTime, char*& start, const char* end)
+{
+ if (start + sizeof(time_t) >= end)
+ return false;
+
+ resultTime = *reinterpret_cast_ptr<time_t*>(start);
+ start += sizeof(time_t);
+
+ return true;
+}
+
+static const char schemaVersion = '1';
+static const char persistentPluginMetadataCacheFilename[] = "PluginMetadataCache.bin";
+
+void PluginDatabase::loadPersistentMetadataCache()
+{
+ if (!isPersistentMetadataCacheEnabled() || persistentMetadataCachePath().isEmpty())
+ return;
+
+ PlatformFileHandle file;
+ String absoluteCachePath = pathByAppendingComponent(persistentMetadataCachePath(), persistentPluginMetadataCacheFilename);
+ file = openFile(absoluteCachePath, OpenForRead);
+
+ if (!isHandleValid(file))
+ return;
+
+ // Mark cache as loaded regardless of success or failure. If
+ // there's error in the cache, we won't try to load it anymore.
+ m_persistentMetadataCacheIsLoaded = true;
+
+ Vector<char> fileContents;
+ fillBufferWithContentsOfFile(file, fileContents);
+ closeFile(file);
+
+ if (fileContents.size() < 2 || fileContents.first() != schemaVersion || fileContents.last() != '\0') {
+ LOG_ERROR("Unable to read plugin metadata cache: corrupt schema");
+ deleteFile(absoluteCachePath);
+ return;
+ }
+
+ char* bufferPos = fileContents.data() + 1;
+ char* end = fileContents.data() + fileContents.size();
+
+ PluginSet cachedPlugins;
+ HashMap<String, time_t> cachedPluginPathsWithTimes;
+ HashMap<String, RefPtr<PluginPackage> > cachedPluginsByPath;
+
+ while (bufferPos < end) {
+ String path;
+ time_t lastModified;
+ String name;
+ String desc;
+ String mimeDesc;
+ if (!(readUTF8String(path, bufferPos, end)
+ && readTime(lastModified, bufferPos, end)
+ && readUTF8String(name, bufferPos, end)
+ && readUTF8String(desc, bufferPos, end)
+ && readUTF8String(mimeDesc, bufferPos, end))) {
+ LOG_ERROR("Unable to read plugin metadata cache: corrupt data");
+ deleteFile(absoluteCachePath);
+ return;
+ }
+
+ // Skip metadata that points to plugins from directories that
+ // are not part of plugin directory list anymore.
+ String pluginDirectoryName = directoryName(path);
+ if (m_pluginDirectories.find(pluginDirectoryName) == WTF::notFound)
+ continue;
+
+ RefPtr<PluginPackage> package = PluginPackage::createPackageFromCache(path, lastModified, name, desc, mimeDesc);
+
+ if (package && cachedPlugins.add(package).second) {
+ cachedPluginPathsWithTimes.add(package->path(), package->lastModified());
+ cachedPluginsByPath.add(package->path(), package);
+ }
+ }
+
+ m_plugins.swap(cachedPlugins);
+ m_pluginsByPath.swap(cachedPluginsByPath);
+ m_pluginPathsWithTimes.swap(cachedPluginPathsWithTimes);
+}
+
+static bool writeUTF8String(PlatformFileHandle file, const String& string)
+{
+ CString utf8String = string.utf8();
+ int length = utf8String.length() + 1;
+ return writeToFile(file, utf8String.data(), length) == length;
+}
+
+static bool writeTime(PlatformFileHandle file, const time_t& time)
+{
+ return writeToFile(file, reinterpret_cast<const char*>(&time), sizeof(time_t)) == sizeof(time_t);
+}
+
+void PluginDatabase::updatePersistentMetadataCache()
+{
+ if (!isPersistentMetadataCacheEnabled() || persistentMetadataCachePath().isEmpty())
+ return;
+
+ makeAllDirectories(persistentMetadataCachePath());
+ String absoluteCachePath = pathByAppendingComponent(persistentMetadataCachePath(), persistentPluginMetadataCacheFilename);
+ deleteFile(absoluteCachePath);
+
+ if (m_plugins.isEmpty())
+ return;
+
+ PlatformFileHandle file;
+ file = openFile(absoluteCachePath, OpenForWrite);
+
+ if (!isHandleValid(file)) {
+ LOG_ERROR("Unable to open plugin metadata cache for saving");
+ return;
+ }
+
+ char localSchemaVersion = schemaVersion;
+ if (writeToFile(file, &localSchemaVersion, 1) != 1) {
+ LOG_ERROR("Unable to write plugin metadata cache schema");
+ closeFile(file);
+ deleteFile(absoluteCachePath);
+ return;
+ }
+
+ PluginSet::const_iterator end = m_plugins.end();
+ for (PluginSet::const_iterator it = m_plugins.begin(); it != end; ++it) {
+ if (!(writeUTF8String(file, (*it)->path())
+ && writeTime(file, (*it)->lastModified())
+ && writeUTF8String(file, (*it)->name())
+ && writeUTF8String(file, (*it)->description())
+ && writeUTF8String(file, (*it)->fullMIMEDescription()))) {
+ LOG_ERROR("Unable to write plugin metadata to cache");
+ closeFile(file);
+ deleteFile(absoluteCachePath);
+ return;
+ }
+ }
+
+ closeFile(file);
+}
+
+bool PluginDatabase::isPersistentMetadataCacheEnabled()
+{
+ return gPersistentPluginMetadataCacheIsEnabled;
+}
+
+void PluginDatabase::setPersistentMetadataCacheEnabled(bool isEnabled)
+{
+ gPersistentPluginMetadataCacheIsEnabled = isEnabled;
+}
+
+String PluginDatabase::persistentMetadataCachePath()
+{
+ return WebCore::persistentPluginMetadataCachePath();
+}
+
+void PluginDatabase::setPersistentMetadataCachePath(const String& persistentMetadataCachePath)
+{
+ WebCore::persistentPluginMetadataCachePath() = persistentMetadataCachePath;
+}
+#endif
+}